]> granicus.if.org Git - php/commitdiff
Added experimental (disabled by default) file based opcode cache.
authorDmitry Stogov <dmitry@zend.com>
Wed, 6 May 2015 20:46:49 +0000 (23:46 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 6 May 2015 20:46:49 +0000 (23:46 +0300)
22 files changed:
NEWS
ext/opcache/ZendAccelerator.c
ext/opcache/ZendAccelerator.h
ext/opcache/config.m4
ext/opcache/config.w32
ext/opcache/tests/001_cli.phpt
ext/opcache/tests/blacklist-win32.phpt
ext/opcache/tests/blacklist.phpt
ext/opcache/tests/bug69281.phpt
ext/opcache/tests/is_script_cached.phpt
ext/opcache/zend_accelerator_module.c
ext/opcache/zend_accelerator_util_funcs.c
ext/opcache/zend_accelerator_util_funcs.h
ext/opcache/zend_file_cache.c [new file with mode: 0644]
ext/opcache/zend_file_cache.h [new file with mode: 0644]
ext/opcache/zend_persist.c
ext/opcache/zend_shared_alloc.c
ext/opcache/zend_shared_alloc.h
ext/phar/tests/create_new_and_modify.phpt
ext/phar/tests/tar/create_new_and_modify.phpt
ext/phar/tests/zip/create_new_and_modify.phpt
run-tests.php

diff --git a/NEWS b/NEWS
index 83d456dad58796fba651e67b2d9dde96b05ad954..81032f978f8649fb14b9747f91ff20fbc6e77e14 100644 (file)
--- a/NEWS
+++ b/NEWS
   . Removed mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb(), mcrypt_ofb(). (Nikita)
 
 - Opcache:
+  . Added experimental (disabled by default) file based opcode cache.
+    (Dmitry, Laruence, Anatol)
   . Fixed bug with try blocks being removed when extended_info opcode
     generation is turned on. (Laruence)
   . Fixed bug #68644 (strlen incorrect : mbstring + func_overload=2 +UTF-8
index ede6efd94c86756ba16b01bf846a523324bad116..847746b01f87504e69ca3a851002f9cae21870cc 100644 (file)
 #include "zend_accelerator_hash.h"
 #include "ext/pcre/php_pcre.h"
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+# include "zend_file_cache.h"
+# include "ext/standard/md5.h"
+#endif
+
 #ifndef ZEND_WIN32
 #include  <netdb.h>
 #endif
@@ -311,6 +316,12 @@ zend_string *accel_new_interned_string(zend_string *str)
        uint idx;
        Bucket *p;
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (ZCG(accel_directives).file_cache_only) {
+               return str;
+       }
+#endif
+
        if (IS_ACCEL_INTERNED(str)) {
                /* this is already an interned string */
                return str;
@@ -727,7 +738,7 @@ static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_ha
 }
 #endif
 
-static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
+accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
 {
        zend_stat_t statbuf;
 #ifdef ZEND_WIN32
@@ -896,29 +907,6 @@ int validate_timestamp_and_record(zend_persistent_script *persistent_script, zen
        }
 }
 
-static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
-{
-       signed char *mem = (signed char*)persistent_script->mem;
-       size_t size = persistent_script->size;
-       size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
-       unsigned int checksum = ADLER32_INIT;
-
-       if (mem < (signed char*)persistent_script) {
-               checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem);
-               size -= (signed char*)persistent_script - mem;
-               mem  += (signed char*)persistent_script - mem;
-       }
-
-       zend_adler32(checksum, mem, persistent_script_check_block_size);
-       mem  += sizeof(*persistent_script);
-       size -= sizeof(*persistent_script);
-
-       if (size > 0) {
-               checksum = zend_adler32(checksum, mem, size);
-       }
-       return checksum;
-}
-
 /* Instead of resolving full real path name each time we need to identify file,
  * we create a key that consist from requested file name, current working
  * directory, current include_path, etc */
@@ -1080,6 +1068,15 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
        zend_persistent_script *persistent_script;
 
        if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+               if (ZCG(accel_directives).file_cache) {
+                       realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
+                       if (realpath) {
+                               zend_file_cache_invalidate(realpath);
+                               zend_string_release(realpath);
+                       }
+               }
+#endif
                return FAILURE;
        }
 
@@ -1089,6 +1086,12 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
                return FAILURE;
        }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (ZCG(accel_directives).file_cache) {
+               zend_file_cache_invalidate(realpath);
+       }
+#endif
+
        persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
        if (persistent_script && !persistent_script->corrupted) {
                zend_file_handle file_handle;
@@ -1118,7 +1121,7 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
        }
 
        accelerator_shm_read_unlock();
-       efree(realpath);
+       zend_string_release(realpath);
 
        return SUCCESS;
 }
@@ -1145,6 +1148,64 @@ static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_ha
        }
 }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, int *from_shared_memory)
+{
+       uint memory_used;
+
+       /* Check if script may be stored in shared memory */
+       if (!zend_accel_script_persistable(new_persistent_script)) {
+               return new_persistent_script;
+       }
+
+       if (!zend_accel_script_optimize(new_persistent_script)) {
+               return new_persistent_script;
+       }
+
+       zend_shared_alloc_init_xlat_table();
+
+       /* Calculate the required memory size */
+       memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0);
+
+       /* Allocate memory block */
+#ifdef __SSE2__
+       /* Align to 64-byte boundary */
+       ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
+       ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
+#else
+       ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
+#endif
+
+       /* Copy into shared memory */
+       new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0);
+
+       zend_shared_alloc_destroy_xlat_table();
+
+       new_persistent_script->is_phar =
+               new_persistent_script->full_path &&
+               strstr(new_persistent_script->full_path->val, ".phar") &&
+               !strstr(new_persistent_script->full_path->val, "://");
+
+       /* Consistency check */
+       if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
+               zend_accel_error(
+                       ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
+                       "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
+                       new_persistent_script->full_path->val,
+                       new_persistent_script->mem,
+                       (char *)new_persistent_script->mem + new_persistent_script->size,
+                       ZCG(mem));
+       }
+
+       new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
+
+       zend_file_cache_script_store(new_persistent_script);
+
+       *from_shared_memory = 1;
+       return new_persistent_script;
+}
+#endif
+
 static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory)
 {
        zend_accel_hash_entry *bucket;
@@ -1188,6 +1249,9 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
                }
        }
 
+
+       zend_shared_alloc_init_xlat_table();
+
        /* Calculate the required memory size */
        memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length);
 
@@ -1200,6 +1264,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
        ZCG(mem) = zend_shared_alloc(memory_used);
 #endif
        if (!ZCG(mem)) {
+               zend_shared_alloc_destroy_xlat_table();
                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
                zend_shared_alloc_unlock();
                return new_persistent_script;
@@ -1208,6 +1273,8 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
        /* Copy into shared memory */
        new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length);
 
+       zend_shared_alloc_destroy_xlat_table();
+
        new_persistent_script->is_phar =
                new_persistent_script->full_path &&
                strstr(new_persistent_script->full_path->val, ".phar") &&
@@ -1250,6 +1317,14 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
 
        zend_shared_alloc_unlock();
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (ZCG(accel_directives).file_cache) {
+               SHM_PROTECT();
+               zend_file_cache_script_store(new_persistent_script);
+               SHM_UNPROTECT();
+       }
+#endif
+
        *from_shared_memory = 1;
        return new_persistent_script;
 }
@@ -1315,7 +1390,7 @@ static void zend_accel_init_auto_globals(void)
        }
 }
 
-static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p, int *from_shared_memory)
+static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p)
 {
        zend_persistent_script *new_persistent_script;
        zend_op_array *orig_active_op_array;
@@ -1460,9 +1535,81 @@ static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_han
        zend_string_hash_val(new_persistent_script->full_path);
 
        /* Now persistent_script structure is ready in process memory */
-       return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory);
+       return new_persistent_script;
 }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
+{
+       zend_persistent_script *persistent_script;
+       zend_op_array *op_array = NULL;
+       int from_memory; /* if the script we've got is stored in SHM */
+
+       if (is_stream_path(file_handle->filename) &&
+           !is_cacheable_stream_path(file_handle->filename)) {
+               return accelerator_orig_compile_file(file_handle, type);
+       }
+
+       if (!file_handle->opened_path) {
+               if (file_handle->type == ZEND_HANDLE_FILENAME &&
+                   accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
+                       if (type == ZEND_REQUIRE) {
+                               zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
+                               zend_bailout();
+                       } else {
+                               zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
+                       }
+                       return NULL;
+           }
+       }
+
+       persistent_script = zend_file_cache_script_load(file_handle);
+       if (persistent_script) {
+               /* see bug #15471 (old BTS) */
+               if (persistent_script->full_path) {
+                       if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
+                           !EG(current_execute_data)->func ||
+                           !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
+                           EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
+                           (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
+                            EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
+                               if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+                                       /* ext/phar has to load phar's metadata into memory */
+                                       if (persistent_script->is_phar) {
+                                               php_stream_statbuf ssb;
+                                               char *fname = emalloc(sizeof("phar://") + persistent_script->full_path->len);
+
+                                               memcpy(fname, "phar://", sizeof("phar://") - 1);
+                                               memcpy(fname + sizeof("phar://") - 1, persistent_script->full_path->val, persistent_script->full_path->len + 1);
+                                               php_stream_stat_path(fname, &ssb);
+                                               efree(fname);
+                                       }
+                               }
+                       }
+               }
+               zend_file_handle_dtor(file_handle);
+
+               persistent_script->dynamic_members.last_used = ZCG(request_time);
+
+           if (persistent_script->ping_auto_globals_mask) {
+                       zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
+               }
+
+               return zend_accel_load_script(persistent_script, 1);
+       }
+
+       persistent_script = opcache_compile_file(file_handle, type, NULL, 0, &op_array);
+
+       if (persistent_script) {
+               from_memory = 0;
+               persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
+               return zend_accel_load_script(persistent_script, from_memory);
+       }
+
+       return op_array;
+}
+#endif
+
 /* zend_compile() replacement */
 zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
 {
@@ -1471,11 +1618,21 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
        int key_length;
        int from_shared_memory; /* if the script we've got is stored in SHM */
 
-       if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok ||
-               (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
-           (ZCSG(restart_in_progress) && accel_restart_is_active())) {
+       if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok) {
                /* The Accelerator is disabled, act as if without the Accelerator */
                return accelerator_orig_compile_file(file_handle, type);
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       } else if (ZCG(accel_directives).file_cache_only) {
+               return file_cache_compile_file(file_handle, type);
+#endif
+       } else if ((!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
+                  (ZCSG(restart_in_progress) && accel_restart_is_active())) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+               if (ZCG(accel_directives).file_cache) {
+                       return file_cache_compile_file(file_handle, type);
+               }
+#endif
+               return accelerator_orig_compile_file(file_handle, type);
        }
 
        /* In case this callback is called from include_once, require_once or it's
@@ -1602,6 +1759,13 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
                }
        }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       /* Check the second level cache */
+       if (!persistent_script && ZCG(accel_directives).file_cache) {
+               persistent_script = zend_file_cache_script_load(file_handle);
+       }
+#endif
+
        /* If script was not found or invalidated by validate_timestamps */
        if (!persistent_script) {
                uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
@@ -1620,7 +1784,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
          * If it isn't compile_and_cache_file() changes the flag to 0
          */
                from_shared_memory = 0;
-               persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory);
+               persistent_script = opcache_compile_file(file_handle, type, key, key ? key_length : 0, &op_array);
+               if (persistent_script) {
+                       persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
+               }
 
                /* Caching is disabled, returning op_array;
                 * or something went wrong during compilation, returning NULL
@@ -1830,7 +1997,6 @@ static void accel_activate(void)
                zend_accel_copy_internal_functions();
        }
 
-       SHM_UNPROTECT();
        /* PHP-5.4 and above return "double", but we use 1 sec precision */
        ZCG(auto_globals_mask) = 0;
        ZCG(request_time) = (time_t)sapi_get_request_time();
@@ -1839,6 +2005,23 @@ static void accel_activate(void)
        ZCG(include_path_key_len) = 0;
        ZCG(include_path_check) = 1;
 
+       /* check if ZCG(function_table) wasn't somehow polluted on the way */
+       if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
+               zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
+       }
+
+       ZCG(cwd) = NULL;
+       ZCG(cwd_key_len) = 0;
+       ZCG(cwd_check) = 1;
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (ZCG(accel_directives).file_cache_only) {
+               return;
+       }
+#endif
+
+       SHM_UNPROTECT();
+
        if (ZCG(counted)) {
 #ifdef ZTS
                zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
@@ -1894,15 +2077,6 @@ static void accel_activate(void)
                zend_shared_alloc_unlock();
        }
 
-       /* check if ZCG(function_table) wasn't somehow polluted on the way */
-       if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
-               zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
-       }
-
-       ZCG(cwd) = NULL;
-       ZCG(cwd_key_len) = 0;
-       ZCG(cwd_check) = 1;
-
        SHM_PROTECT();
 
        if (ZCSG(last_restart_time) != ZCG(last_restart_time)) {
@@ -2285,6 +2459,38 @@ static void accel_globals_dtor(zend_accel_globals *accel_globals)
        }
 }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+
+#define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_CHAR) ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT)
+
+static void accel_gen_system_id(void)
+{
+       PHP_MD5_CTX context;
+       unsigned char digest[16], c;
+       char *md5str = ZCG(system_id);
+       int i;
+
+       PHP_MD5Init(&context);
+       PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
+       PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
+       PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
+       if (strstr(PHP_VERSION, "-dev") != 0) {
+               /* Development versions may be changed from build to build */
+               PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
+               PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
+       }
+       PHP_MD5Final(digest, &context);
+       for (i = 0; i < 16; i++) {
+               c = digest[i] >> 4;
+               c = (c <= 9) ? c + '0' : c - 10 + 'a';
+               md5str[i * 2] = c;
+               c = digest[i] &  0x0f;
+               c = (c <= 9) ? c + '0' : c - 10 + 'a';
+               md5str[(i * 2) + 1] = c;
+       }
+}
+#endif
+
 static int accel_startup(zend_extension *extension)
 {
        zend_function *func;
@@ -2306,6 +2512,10 @@ static int accel_startup(zend_extension *extension)
                return FAILURE;
        }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       accel_gen_system_id();
+#endif
+
        /* no supported SAPI found - disable acceleration and stop initialization */
        if (accel_find_sapi() == FAILURE) {
                accel_startup_ok = 0;
@@ -2321,49 +2531,73 @@ static int accel_startup(zend_extension *extension)
        if (ZCG(enabled) == 0) {
                return SUCCESS ;
        }
+
 /********************************************/
 /* End of non-SHM dependent initializations */
 /********************************************/
-       switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
-               case ALLOC_SUCCESS:
-                       if (zend_accel_init_shm() == FAILURE) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (!ZCG(accel_directives).file_cache_only) {
+#else
+       if (1) {
+#endif
+               switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
+                       case ALLOC_SUCCESS:
+                               if (zend_accel_init_shm() == FAILURE) {
+                                       accel_startup_ok = 0;
+                                       return FAILURE;
+                               }
+                               break;
+                       case ALLOC_FAILURE:
                                accel_startup_ok = 0;
-                               return FAILURE;
-                       }
-                       break;
-               case ALLOC_FAILURE:
-                       accel_startup_ok = 0;
-                       zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
-                       return SUCCESS;
-               case SUCCESSFULLY_REATTACHED:
-                       accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
-                       zend_shared_alloc_lock();
-                       orig_new_interned_string = zend_new_interned_string;
-                       orig_interned_strings_snapshot = zend_interned_strings_snapshot;
-                       orig_interned_strings_restore = zend_interned_strings_restore;
-
-                       zend_new_interned_string = accel_new_interned_string_for_php;
-                       zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
-                       zend_interned_strings_restore = accel_interned_strings_restore_for_php;
+                               zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
+                               return SUCCESS;
+                       case SUCCESSFULLY_REATTACHED:
+                               accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
+                               zend_shared_alloc_lock();
+                               orig_new_interned_string = zend_new_interned_string;
+                               orig_interned_strings_snapshot = zend_interned_strings_snapshot;
+                               orig_interned_strings_restore = zend_interned_strings_restore;
+
+                               zend_new_interned_string = accel_new_interned_string_for_php;
+                               zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
+                               zend_interned_strings_restore = accel_interned_strings_restore_for_php;
 #ifndef ZTS
-                       accel_use_shm_interned_strings();
+                               accel_use_shm_interned_strings();
 #endif
-                       zend_shared_alloc_unlock();
-                       break;
-               case FAILED_REATTACHED:
-                       accel_startup_ok = 0;
-                       zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
-                       return SUCCESS;
-                       break;
-       }
+                               zend_shared_alloc_unlock();
+                               break;
+                       case FAILED_REATTACHED:
+                               accel_startup_ok = 0;
+                               zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
+                               return SUCCESS;
+                               break;
+               }
 
-       /* remeber the last restart time in the process memory */
-       ZCG(last_restart_time) = ZCSG(last_restart_time);
+               /* from this point further, shared memory is supposed to be OK */
 
-       /* from this point further, shared memory is supposed to be OK */
+               /* remeber the last restart time in the process memory */
+               ZCG(last_restart_time) = ZCSG(last_restart_time);
+
+               /* Init auto-global strings */
+               zend_accel_init_auto_globals();
+
+               zend_shared_alloc_lock();
+               zend_shared_alloc_save_state();
+               zend_shared_alloc_unlock();
+
+               SHM_PROTECT();
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       } else if (!ZCG(accel_directives).file_cache) {
+               accel_startup_ok = 0;
+               zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
+               return SUCCESS;
+       } else {
+               accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
 
-       /* Init auto-global strings */
-       zend_accel_init_auto_globals();
+               /* Init auto-global strings */
+               zend_accel_init_auto_globals();
+#endif
+       }
 
        /* Override compiler */
        accelerator_orig_compile_file = zend_compile_file;
@@ -2395,12 +2629,6 @@ static int accel_startup(zend_extension *extension)
                ini_entry->on_modify = accel_include_path_on_modify;
        }
 
-       zend_shared_alloc_lock();
-       zend_shared_alloc_save_state();
-       zend_shared_alloc_unlock();
-
-       SHM_PROTECT();
-
        accel_startup_ok = 1;
 
        /* Override file_exists(), is_file() and is_readable() */
@@ -2430,6 +2658,7 @@ static void accel_free_ts_resources()
 void accel_shutdown(void)
 {
        zend_ini_entry *ini_entry;
+       zend_bool file_cache_only = 0;
 
        zend_accel_blacklist_shutdown(&accel_blacklist);
 
@@ -2453,8 +2682,15 @@ void accel_shutdown(void)
        zend_interned_strings_snapshot = orig_interned_strings_snapshot;
        zend_interned_strings_restore = orig_interned_strings_restore;
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       file_cache_only = ZCG(accel_directives).file_cache_only;
+#endif
+
        accel_free_ts_resources();
-       zend_shared_alloc_shutdown();
+
+       if (!file_cache_only) {
+               zend_shared_alloc_shutdown();
+       }
        zend_compile_file = accelerator_orig_compile_file;
 
        if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
index a41d0a94d0bfab5c7aa98994d7ef4184870ec1f1..2c6584a1e2a28e0ff90dc57d991b36ddde71fddb 100644 (file)
@@ -215,6 +215,11 @@ typedef struct _zend_accel_directives {
        zend_long           max_file_size;
        zend_long           interned_strings_buffer;
        char          *restrict_api;
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       char          *file_cache;
+       zend_bool      file_cache_only;
+       zend_bool      file_cache_consistency_checks;
+#endif
 } zend_accel_directives;
 
 typedef struct _zend_accel_globals {
@@ -238,6 +243,10 @@ typedef struct _zend_accel_globals {
        int                     auto_globals_mask;
        time_t                  request_time;
        time_t                  last_restart_time; /* used to synchronize SHM and in-process caches */
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       char                    system_id[32];
+#endif
+       HashTable               xlat_table;
        /* preallocated shared-memory block to save current script */
        void                   *mem;
        void                   *arena_mem;
@@ -302,6 +311,7 @@ extern char *zps_api_failure_reason;
 void accel_shutdown(void);
 void zend_accel_schedule_restart(zend_accel_restart_reason reason);
 void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason);
+accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size);
 int  validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
 int  zend_accel_invalidate(const char *filename, int filename_len, zend_bool force);
 int  zend_accel_script_optimize(zend_persistent_script *persistent_script);
index b10567b468962da96fa4a586214b16d9d82fb6cb..856f24ab58b2df8d2a72c8010c482d9043700a40 100644 (file)
@@ -5,8 +5,15 @@ dnl
 PHP_ARG_ENABLE(opcache, whether to enable Zend OPcache support,
 [  --disable-opcache       Disable Zend OPcache support], yes)
 
+PHP_ARG_ENABLE(opcache-file, whether to enable file based caching (experimental),
+[  --enable-opcache-file   Enable file based caching], no)
+
 if test "$PHP_OPCACHE" != "no"; then
 
+  if test "$PHP_OPCACHE_FILE" == "yes"; then
+    AC_DEFINE(HAVE_OPCACHE_FILE_CACHE, 1, [Define to enable file based caching (experimental)])
+  fi
+
   AC_CHECK_FUNC(mprotect,[
     AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function])
   ])
@@ -371,6 +378,7 @@ fi
        zend_accelerator_module.c \
        zend_persist.c \
        zend_persist_calc.c \
+       zend_file_cache.c \
        zend_shared_alloc.c \
        zend_accelerator_util_funcs.c \
        shared_alloc_shm.c \
index edcbc320c6cd149ee5745fd4276839fccec0a08a..dcc2501ffc735aa18edb4dfb7e61087b160bc884 100644 (file)
@@ -1,9 +1,15 @@
 ARG_ENABLE("opcache", "whether to enable Zend OPcache support", "yes");
 
+ARG_ENABLE("opcache-file", "whether to enable file based caching (experimental)", "no");
+
 var PHP_OPCACHE_PGO = false;
 
 if (PHP_OPCACHE != "no") {
 
+       if (PHP_OPCACHE_FILE == "yes") {
+               AC_DEFINE('HAVE_OPCACHE_FILE_CACHE', 1, 'Define to enable file based caching (experimental)');
+       }
+
        EXTENSION('opcache', "\
                ZendAccelerator.c \
                zend_accelerator_blacklist.c \
@@ -13,6 +19,7 @@ if (PHP_OPCACHE != "no") {
                zend_accelerator_util_funcs.c \
                zend_persist.c \
                zend_persist_calc.c \
+               zend_file_cache.c \
                zend_shared_alloc.c \
                shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
 
index c51db23f56fb4feff85e7cf64081ebeee45214d8..3be1a52228ebb6854c4b442aa9a730b49c8dbdfd 100644 (file)
@@ -3,6 +3,7 @@
 --INI--
 opcache.enable=1
 opcache.enable_cli=1
+opcache.file_cache_only=0
 --SKIPIF--
 <?php require_once('skipif.inc'); ?>
 --FILE--
index 909c695fcd6ba7ef513c4994503fa97a2b81d392..1e479b6c2e2e650a84d695728813755baeb62316 100644 (file)
@@ -5,6 +5,7 @@ opcache.enable=1
 opcache.enable_cli=1
 opcache.blacklist_filename={PWD}/opcache-*.blacklist
 opcache.file_update_protection=0
+opcache.file_cache_only=0
 --SKIPIF--
 <?php require_once('skipif.inc'); ?>
 <?php if (substr(PHP_OS, 0, 3) != 'WIN') {  die('skip only for Windows'); } ?>
index 0c60425dac2e4bb34b014604700615db96e88fb9..4e9a0f16fcb6779d1baa3056650b865daa9a61ae 100644 (file)
@@ -5,6 +5,7 @@ opcache.enable=1
 opcache.enable_cli=1
 opcache.blacklist_filename={PWD}/opcache-*.blacklist
 opcache.file_update_protection=0
+opcache.file_cache_only=0
 --SKIPIF--
 <?php require_once('skipif.inc'); ?>
 <?php if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip not for Windows'); } ?>
index 4d68d5007b1b08d4b46b560725afc5480737a18b..506f466ac897ae55d80c234719b48b6ac168eeb4 100644 (file)
@@ -5,6 +5,7 @@ opcache.enable=1
 opcache.enable_cli=1
 opcache.file_update_protection=0
 opcache.validate_timestamps=0
+opcache.file_cache_only=0
 --SKIPIF--
 <?php require_once('skipif.inc'); ?>
 --FILE--
index bac561103f5c47b9463941d5c2e9790295c0d107..c3ab054251a8efdea007290309283aaab5404c40 100644 (file)
@@ -5,6 +5,7 @@ opcache.enable=1
 opcache.enable_cli=1
 opcache.file_update_protection=0
 opcache.validate_timestamps=1
+opcache.file_cache_only=0
 --SKIPIF--
 <?php require_once('skipif.inc'); ?>
 --FILE--
index b79816b8f9970539a1e26981e54f359fe0319e9a..11c0635090eab2bda1263fd34bc5ac4ee4e9c41d 100644 (file)
@@ -241,6 +241,38 @@ static ZEND_INI_MH(OnEnable)
        }
 }
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+
+#ifndef S_ISDIR
+# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
+#endif
+
+static ZEND_INI_MH(OnUpdateFileCache)
+{
+       if (new_value) {
+               if (!new_value->len) {
+                       new_value = NULL;
+               } else {
+                       zend_stat_t buf;
+
+                   if (!IS_ABSOLUTE_PATH(new_value->val, new_value->len) ||
+                           zend_stat(new_value->val, &buf) != 0 ||
+                           !S_ISDIR(buf.st_mode) ||
+#ifndef ZEND_WIN32
+                               access(new_value->val, R_OK | W_OK | X_OK) != 0) {
+#else
+                               _access(new_value->val, 06) != 0) {
+#endif
+                               zend_accel_error(ACCEL_LOG_WARNING, "opcache.file_cache must be a full path of accessable directory.\n");
+                               new_value = NULL;
+                       }
+               }
+       }
+       OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
+       return SUCCESS;
+}
+#endif
+
 ZEND_INI_BEGIN()
        STD_PHP_INI_BOOLEAN("opcache.enable"             , "1", PHP_INI_ALL,    OnEnable,     enabled                             , zend_accel_globals, accel_globals)
        STD_PHP_INI_BOOLEAN("opcache.use_cwd"            , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd            , zend_accel_globals, accel_globals)
@@ -276,6 +308,12 @@ ZEND_INI_BEGIN()
 #ifdef ZEND_WIN32
        STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM,    OnUpdateString,                              accel_directives.mmap_base,                 zend_accel_globals, accel_globals)
 #endif
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       STD_PHP_INI_ENTRY("opcache.file_cache"                    , NULL  , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache,                    zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("opcache.file_cache_only"               , "0"   , PHP_INI_SYSTEM, OnUpdateBool,          accel_directives.file_cache_only,               zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("opcache.file_cache_consistency_checks" , "1"   , PHP_INI_SYSTEM, OnUpdateBool,          accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals)
+#endif
 ZEND_INI_END()
 
 static int filename_is_in_cache(zend_string *filename)
@@ -395,6 +433,25 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
        } else {
                php_info_print_table_row(2, "Optimization", "Disabled");
        }
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (!ZCG(accel_directives).file_cache_only) {
+               php_info_print_table_row(2, "SHM Cache", "Enabled");
+       } else {
+               php_info_print_table_row(2, "SHM Cache", "Disabled");
+       }
+       if (ZCG(accel_directives).file_cache) {
+               php_info_print_table_row(2, "File Cache", "Enabled");
+       } else {
+               php_info_print_table_row(2, "File Cache", "Disabled");
+       }
+       if (ZCG(accel_directives).file_cache_only) {
+               if (!accel_startup_ok || zps_api_failure_reason) {
+                       php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
+               } else {
+                       php_info_print_table_row(2, "Startup", "OK");
+               }
+       } else
+#endif
        if (ZCG(enabled)) {
                if (!accel_startup_ok || zps_api_failure_reason) {
                        php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
@@ -529,6 +586,17 @@ static ZEND_FUNCTION(opcache_get_status)
 
        /* Trivia */
        add_assoc_bool(return_value, "opcache_enabled", ZCG(enabled) && (ZCG(counted) || ZCSG(accelerator_enabled)));
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       if (ZCG(accel_directives).file_cache) {
+               add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache);
+       }
+       if (ZCG(accel_directives).file_cache_only) {
+               add_assoc_bool(return_value, "file_cache_only", 1);
+               return;
+       }
+#endif
+
        add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
        add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending));
        add_assoc_bool(return_value, "restart_in_progress", ZCSG(restart_in_progress));
@@ -630,6 +698,12 @@ static ZEND_FUNCTION(opcache_get_configuration)
        add_assoc_bool(&directives,   "opcache.enable_file_override",   ZCG(accel_directives).file_override_enabled);
        add_assoc_long(&directives,      "opcache.optimization_level",     ZCG(accel_directives).optimization_level);
 
+#ifdef HAVE_OPCACHE_FILE_CACHE
+       add_assoc_string(&directives, "opcache.file_cache",                    ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : "");
+       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);
+#endif
+
        add_assoc_zval(return_value, "directives", &directives);
 
        /*version */
index 92ada0b17fa3367a630b3a4463d59583a08bd23b..9407b2ec3977c716324bdd76da5ef19294d74ffe 100644 (file)
@@ -97,7 +97,7 @@ void free_persistent_script(zend_persistent_script *persistent_script, int destr
        zend_hash_destroy(&persistent_script->class_table);
 
        if (persistent_script->full_path) {
-               efree(persistent_script->full_path);
+               zend_string_release(persistent_script->full_path);
        }
 
        efree(persistent_script);
@@ -929,3 +929,26 @@ unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len)
 
        return (s2 << 16) | s1;
 }
+
+unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
+{
+       signed char *mem = (signed char*)persistent_script->mem;
+       size_t size = persistent_script->size;
+       size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
+       unsigned int checksum = ADLER32_INIT;
+
+       if (mem < (signed char*)persistent_script) {
+               checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem);
+               size -= (signed char*)persistent_script - mem;
+               mem  += (signed char*)persistent_script - mem;
+       }
+
+       zend_adler32(checksum, mem, persistent_script_check_block_size);
+       mem  += sizeof(*persistent_script);
+       size -= sizeof(*persistent_script);
+
+       if (size > 0) {
+               checksum = zend_adler32(checksum, mem, size);
+       }
+       return checksum;
+}
index 2ef159584927c1cbaa8e2fc88375f0206e04e049..e2d8433145a4b2782655b3e1b945c29ed6c854a6 100644 (file)
@@ -39,6 +39,8 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
 
 unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len);
 
+unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script);
+
 #endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */
 
 /*
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
new file mode 100644 (file)
index 0000000..61c2069
--- /dev/null
@@ -0,0 +1,1301 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend OPcache                                                         |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2015 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:           |
+   | http://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: Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_virtual_cwd.h"
+#include "zend_compile.h"
+#include "zend_vm.h"
+
+#include "php.h"
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+
+#include "ZendAccelerator.h"
+#include "zend_file_cache.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_util_funcs.h"
+#include "zend_accelerator_hash.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+#endif
+
+#ifdef ZEND_WIN32
+# define LOCK_SH 0
+# define LOCK_EX 1
+# define LOCK_UN 2
+static int zend_file_cache_flock(int fd, int op)
+{
+       OVERLAPPED offset = {0,0,0,0,NULL};
+       if (op == LOCK_EX) {
+               if (LockFileEx((HANDLE)_get_osfhandle(fd),
+                              LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
+                       return 0;
+               }
+       } else if (op == LOCK_SH) {
+               if (LockFileEx((HANDLE)_get_osfhandle(fd),
+                              0, 0, 1, 0, &offset) == TRUE) {
+                       return 0;
+               }
+       } else if (op == LOCK_UN) {
+               if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
+                                0, 1, 0, &offset) == TRUE) {
+                       return 0;
+               }
+       }
+       return -1;
+}
+#elif defined(HAVE_FLOCK)
+# define zend_file_cache_flock flock
+#else
+# define LOCK_SH 0
+# define LOCK_EX 1
+# define LOCK_UN 2
+static int zend_file_cache_flock(int fd, int type)
+{
+       return 0;
+}
+#endif
+
+#ifndef O_BINARY
+#  define O_BINARY 0
+#endif
+
+#define SUFFIX ".bin"
+
+#define IS_SERIALIZED_INTERNED(ptr) \
+       ((size_t)(ptr) & Z_UL(1))
+#define IS_SERIALIZED(ptr) \
+       ((char*)(ptr) < (char*)script->size)
+#define IS_UNSERIALIZED(ptr) \
+       (((char*)(ptr) >= (char*)script->mem && (char*)(ptr) < (char*)script->mem + script->size) || \
+        IS_ACCEL_INTERNED(ptr))
+#define SERIALIZE_PTR(ptr) do { \
+               if (ptr) { \
+                       if (IS_ACCEL_INTERNED(ptr)) { \
+                               (ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
+                       } else { \
+                               ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
+                               (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
+                       } \
+               } \
+       } while (0)
+#define UNSERIALIZE_PTR(ptr) do { \
+               if (ptr) { \
+                       if (IS_SERIALIZED_INTERNED(ptr)) { \
+                               (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr)); \
+                       } else { \
+                               ZEND_ASSERT(IS_SERIALIZED(ptr)); \
+                               (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
+                       } \
+               } \
+       } while (0)
+
+static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
+       {HT_INVALID_IDX, HT_INVALID_IDX};
+
+typedef struct _zend_file_cache_metainfo {
+       char         magic[8];
+       char         system_id[32];
+       size_t       mem_size;
+       size_t       str_size;
+       size_t       script_offset;
+       accel_time_t timestamp;
+       uint32_t     checksum;
+} zend_file_cache_metainfo;
+
+static int zend_file_cache_mkdir(char *filename, size_t start)
+{
+       char *s = filename + start;
+
+       while (*s) {
+               if (IS_SLASH(*s)) {
+                       char old = *s;
+                       *s = '\000';
+                       if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
+                               *s = old;
+                               return FAILURE;
+                       }
+                       *s = old;
+               }
+               s++;
+       }
+       return SUCCESS;
+}
+
+typedef void (*serialize_callback_t)(zval                     *zv,
+                                     zend_persistent_script   *script,
+                                     zend_file_cache_metainfo *info,
+                                     void                     *buf);
+
+typedef void (*unserialize_callback_t)(zval                    *zv,
+                                       zend_persistent_script  *script,
+                                       void                    *buf);
+
+static void zend_file_cache_serialize_zval(zval                     *zv,
+                                           zend_persistent_script   *script,
+                                           zend_file_cache_metainfo *info,
+                                           void                     *buf);
+static void zend_file_cache_unserialize_zval(zval                    *zv,
+                                             zend_persistent_script  *script,
+                                             void                    *buf);
+
+static void *zend_file_cache_serialize_interned(zend_string              *str,
+                                                zend_file_cache_metainfo *info)
+{
+       size_t len;
+       void *ret;
+
+       /* check if the same interned string was already stored */
+       ret = zend_shared_alloc_get_xlat_entry(str);
+       if (ret) {
+               return ret;
+       }
+
+       len = ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + str->len + 1);
+       ret = (void*)(info->str_size | Z_UL(1));
+       zend_shared_alloc_register_xlat_entry(str, ret);
+       if (info->str_size + len > ((zend_string*)ZCG(mem))->len) {
+               size_t new_len = info->str_size + len;
+               ZCG(mem) = (void*)zend_string_realloc(
+                       (zend_string*)ZCG(mem),
+                       ((_STR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_STR_HEADER_SIZE + 1),
+                       0);
+       }
+       memcpy(((zend_string*)ZCG(mem))->val + info->str_size, str, len);
+       info->str_size += len;
+       return ret;
+}
+
+static void *zend_file_cache_unserialize_interned(zend_string *str)
+{
+       zend_string *ret;
+
+       str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
+       ret = accel_new_interned_string(str);
+       if (ret == str) {
+               /* String wasn't interned but we will use it as interned anyway */
+               GC_FLAGS(ret) |= IS_STR_INTERNED | IS_STR_PERMANENT;
+       }
+       return ret;
+}
+
+static void zend_file_cache_serialize_hash(HashTable                *ht,
+                                           zend_persistent_script   *script,
+                                           zend_file_cache_metainfo *info,
+                                           void                     *buf,
+                                           serialize_callback_t      func)
+{
+       Bucket *p, *end;
+
+       if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
+               ht->arData = NULL;
+               return;
+       }
+       if (IS_SERIALIZED(ht->arData)) {
+               return;
+       }
+       SERIALIZE_PTR(ht->arData);
+       p = ht->arData;
+       UNSERIALIZE_PTR(p);
+       end = p + ht->nNumUsed;
+       while (p < end) {
+               if (Z_TYPE(p->val) != IS_UNDEF) {
+                       SERIALIZE_PTR(p->key);
+                       func(&p->val, script, info, buf);
+               }
+               p++;
+       }
+}
+
+static zend_ast *zend_file_cache_serialize_ast(zend_ast                 *ast,
+                                               zend_persistent_script   *script,
+                                               zend_file_cache_metainfo *info,
+                                               void                     *buf)
+{
+       uint32_t i;
+       zend_ast *ret;
+
+       SERIALIZE_PTR(ast);
+       ret = ast;
+       UNSERIALIZE_PTR(ast);
+
+       if (ast->kind == ZEND_AST_ZVAL) {
+               zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
+       } else if (zend_ast_is_list(ast)) {
+               zend_ast_list *list = zend_ast_get_list(ast);
+               for (i = 0; i < list->children; i++) {
+                       if (list->child[i]) {
+                               list->child[i] = zend_file_cache_serialize_ast(list->child[i], script, info, buf);
+                       }
+               }
+       } else {
+               uint32_t children = zend_ast_get_num_children(ast);
+               for (i = 0; i < children; i++) {
+                       if (ast->child[i]) {
+                               ast->child[i] = zend_file_cache_serialize_ast(ast->child[i], script, info, buf);
+                       }
+               }
+       }
+       return ret;
+}
+
+static void zend_file_cache_serialize_zval(zval                     *zv,
+                                           zend_persistent_script   *script,
+                                           zend_file_cache_metainfo *info,
+                                           void                     *buf)
+{
+       switch (Z_TYPE_P(zv)) {
+               case IS_STRING:
+               case IS_CONSTANT:
+                       if (!IS_SERIALIZED(Z_STR_P(zv))) {
+                               SERIALIZE_PTR(Z_STR_P(zv));
+                       }
+                       break;
+               case IS_ARRAY:
+                       if (!IS_SERIALIZED(Z_ARR_P(zv))) {
+                               HashTable *ht;
+
+                               SERIALIZE_PTR(Z_ARR_P(zv));
+                               ht = Z_ARR_P(zv);
+                               UNSERIALIZE_PTR(ht);
+                               zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
+                       }
+                       break;
+               case IS_REFERENCE:
+                       if (!IS_SERIALIZED(Z_REF_P(zv))) {
+                               zend_reference *ref;
+
+                               SERIALIZE_PTR(Z_REF_P(zv));
+                               ref = Z_REF_P(zv);
+                               UNSERIALIZE_PTR(ref);
+                               zend_file_cache_serialize_zval(&ref->val, script, info, buf);
+                       }
+                       break;
+               case IS_CONSTANT_AST:
+                       if (!IS_SERIALIZED(Z_AST_P(zv))) {
+                               zend_ast_ref *ast;
+
+                               SERIALIZE_PTR(Z_AST_P(zv));
+                               ast = Z_AST_P(zv);
+                               UNSERIALIZE_PTR(ast);
+                               if (!IS_SERIALIZED(ast->ast)) {
+                                       ast->ast = zend_file_cache_serialize_ast(ast->ast, script, info, buf);
+                               }
+                       }
+                       break;
+       }
+}
+
+static void zend_file_cache_serialize_op_array(zend_op_array            *op_array,
+                                               zend_persistent_script   *script,
+                                               zend_file_cache_metainfo *info,
+                                               void                     *buf)
+{
+       if (op_array->static_variables && !IS_SERIALIZED(op_array->static_variables)) {
+               HashTable *ht;
+
+               SERIALIZE_PTR(op_array->static_variables);
+               ht = op_array->static_variables;
+               UNSERIALIZE_PTR(ht);
+               zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
+       }
+
+       if (op_array->literals && !IS_SERIALIZED(op_array->literals)) {
+               zval *p, *end;
+
+               SERIALIZE_PTR(op_array->literals);
+               p = op_array->literals;
+               UNSERIALIZE_PTR(p);
+               end = p + op_array->last_literal;
+               while (p < end) {
+                       zend_file_cache_serialize_zval(p, script, info, buf);
+                       p++;
+               }
+       }
+
+       if (!IS_SERIALIZED(op_array->opcodes)) {
+#if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
+               zend_op *opline, *end;
+
+               SERIALIZE_PTR(op_array->opcodes);
+               opline = op_array->opcodes;
+               UNSERIALIZE_PTR(opline);
+               end = opline + op_array->last;
+               while (opline < end) {
+# if ZEND_USE_ABS_CONST_ADDR
+                       if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                               SERIALIZE_PTR(opline->op1.zv);
+                       }
+                       if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                               SERIALIZE_PTR(opline->op2.zv);
+                       }
+# endif
+# if ZEND_USE_ABS_JMP_ADDR
+                       switch (opline->opcode) {
+                               case ZEND_JMP:
+                               case ZEND_GOTO:
+                               case ZEND_FAST_CALL:
+                                       SERIALIZE_PTR(opline->op1.jmp_addr);
+                                       break;
+                               case ZEND_JMPZNZ:
+                                       /* relative extended_value don't have to be changed */
+                                       /* break omitted intentionally */
+                               case ZEND_JMPZ:
+                               case ZEND_JMPNZ:
+                               case ZEND_JMPZ_EX:
+                               case ZEND_JMPNZ_EX:
+                               case ZEND_JMP_SET:
+                               case ZEND_COALESCE:
+                               case ZEND_NEW:
+                               case ZEND_FE_RESET_R:
+                               case ZEND_FE_RESET_RW:
+                               case ZEND_FE_FETCH_R:
+                               case ZEND_FE_FETCH_RW:
+                               case ZEND_ASSERT_CHECK:
+                                       SERIALIZE_PTR(opline->op2.jmp_addr);
+                                       break;
+                       }
+# endif
+                       opline++;
+               }
+#else
+               SERIALIZE_PTR(op_array->opcodes);
+#endif
+
+               if (op_array->arg_info) {
+                       zend_arg_info *p, *end;
+                       SERIALIZE_PTR(op_array->arg_info);
+                       p = op_array->arg_info;
+                       UNSERIALIZE_PTR(p);
+                       end = p + op_array->num_args;
+                       if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+                               p--;
+                       }
+                       if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
+                               end++;
+                       }
+                       while (p < end) {
+                               if (!IS_SERIALIZED(p->name)) {
+                                       SERIALIZE_PTR(p->name);
+                               }
+                               if (!IS_SERIALIZED(p->class_name)) {
+                                       SERIALIZE_PTR(p->class_name);
+                               }
+                               p++;
+                       }
+               }
+
+               if (op_array->vars) {
+                       zend_string **p, **end;
+
+                       SERIALIZE_PTR(op_array->vars);
+                       p = op_array->vars;
+                       UNSERIALIZE_PTR(p);
+                       end = p + op_array->last_var;
+                       while (p < end) {
+                               if (!IS_SERIALIZED(*p)) {
+                                       SERIALIZE_PTR(*p);
+                               }
+                               p++;
+                       }
+               }
+
+               SERIALIZE_PTR(op_array->function_name);
+               SERIALIZE_PTR(op_array->filename);
+               SERIALIZE_PTR(op_array->brk_cont_array);
+               SERIALIZE_PTR(op_array->scope);
+               SERIALIZE_PTR(op_array->doc_comment);
+               SERIALIZE_PTR(op_array->try_catch_array);
+               SERIALIZE_PTR(op_array->prototype);
+       }
+}
+
+static void zend_file_cache_serialize_func(zval                     *zv,
+                                           zend_persistent_script   *script,
+                                           zend_file_cache_metainfo *info,
+                                           void                     *buf)
+{
+       zend_op_array *op_array;
+
+       SERIALIZE_PTR(Z_PTR_P(zv));
+       op_array = Z_PTR_P(zv);
+       UNSERIALIZE_PTR(op_array);
+       zend_file_cache_serialize_op_array(op_array, script, info, buf);
+}
+
+static void zend_file_cache_serialize_prop_info(zval                     *zv,
+                                                zend_persistent_script   *script,
+                                                zend_file_cache_metainfo *info,
+                                                void                     *buf)
+{
+       if (!IS_SERIALIZED(Z_PTR_P(zv))) {
+               zend_property_info *prop;
+
+               SERIALIZE_PTR(Z_PTR_P(zv));
+               prop = Z_PTR_P(zv);
+               UNSERIALIZE_PTR(prop);
+
+               if (prop->ce && !IS_SERIALIZED(prop->ce)) {
+                       SERIALIZE_PTR(prop->ce);
+               }
+               if (prop->name && !IS_SERIALIZED(prop->name)) {
+                       SERIALIZE_PTR(prop->name);
+               }
+               if (prop->doc_comment && !IS_SERIALIZED(prop->doc_comment)) {
+                       SERIALIZE_PTR(prop->doc_comment);
+               }
+       }
+}
+
+static void zend_file_cache_serialize_class(zval                     *zv,
+                                            zend_persistent_script   *script,
+                                            zend_file_cache_metainfo *info,
+                                            void                     *buf)
+{
+       zend_class_entry *ce;
+
+       SERIALIZE_PTR(Z_PTR_P(zv));
+       ce = Z_PTR_P(zv);
+       UNSERIALIZE_PTR(ce);
+
+       SERIALIZE_PTR(ce->name);
+       zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
+       if (ce->default_properties_table) {
+               zval *p, *end;
+
+               SERIALIZE_PTR(ce->default_properties_table);
+               p = ce->default_properties_table;
+               UNSERIALIZE_PTR(p);
+               end = p + ce->default_properties_count;
+               while (p < end) {
+                       zend_file_cache_serialize_zval(p, script, info, buf);
+                       p++;
+               }
+       }
+       if (ce->default_static_members_table) {
+               zval *p, *end;
+
+               SERIALIZE_PTR(ce->default_static_members_table);
+               p = ce->default_static_members_table;
+               UNSERIALIZE_PTR(p);
+               end = p + ce->default_static_members_count;
+               while (p < end) {
+                       zend_file_cache_serialize_zval(p, script, info, buf);
+                       p++;
+               }
+       }
+       zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval);
+       SERIALIZE_PTR(ZEND_CE_FILENAME(ce));
+       SERIALIZE_PTR(ZEND_CE_DOC_COMMENT(ce));
+       zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
+
+       if (ce->trait_aliases) {
+               zend_trait_alias **p, *q;
+
+               SERIALIZE_PTR(ce->trait_aliases);
+               p = ce->trait_aliases;
+               UNSERIALIZE_PTR(p);
+
+               while (*p) {
+                       SERIALIZE_PTR(*p);
+                       q = *p;
+                       UNSERIALIZE_PTR(q);
+
+                       if (q->trait_method) {
+                               zend_trait_method_reference *m;
+
+                               SERIALIZE_PTR(q->trait_method);
+                               m = q->trait_method;
+                               UNSERIALIZE_PTR(m);
+
+                               if (m->method_name) {
+                                       SERIALIZE_PTR(m->method_name);
+                               }
+                               if (m->class_name) {
+                                       SERIALIZE_PTR(m->class_name);
+                               }
+                       }
+
+                       if (q->alias) {
+                               SERIALIZE_PTR(q->alias);
+                       }
+                       p++;
+               }
+       }
+
+       if (ce->trait_precedences) {
+               zend_trait_precedence **p, *q;
+
+               SERIALIZE_PTR(ce->trait_precedences);
+               p = ce->trait_precedences;
+               UNSERIALIZE_PTR(p);
+
+               while (*p) {
+                       SERIALIZE_PTR(*p);
+                       q = *p;
+                       UNSERIALIZE_PTR(q);
+
+                       if (q->trait_method) {
+                               zend_trait_method_reference *m;
+
+                               SERIALIZE_PTR(q->trait_method);
+                               m = q->trait_method;
+                               UNSERIALIZE_PTR(m);
+
+                               if (m->method_name) {
+                                       SERIALIZE_PTR(m->method_name);
+                               }
+                               if (m->class_name) {
+                                       SERIALIZE_PTR(m->class_name);
+                               }
+                       }
+
+                       if (q->exclude_from_classes) {
+                               zend_string **s;
+
+                               SERIALIZE_PTR(q->exclude_from_classes);
+                               s = (zend_string**)q->exclude_from_classes;
+                               UNSERIALIZE_PTR(s);
+
+                               while (*s) {
+                                       SERIALIZE_PTR(*s);
+                                       s++;
+                               }
+                       }
+                       p++;
+               }
+       }
+
+       SERIALIZE_PTR(ce->parent);
+       SERIALIZE_PTR(ce->constructor);
+       SERIALIZE_PTR(ce->destructor);
+       SERIALIZE_PTR(ce->clone);
+       SERIALIZE_PTR(ce->__get);
+       SERIALIZE_PTR(ce->__set);
+       SERIALIZE_PTR(ce->__call);
+       SERIALIZE_PTR(ce->serialize_func);
+       SERIALIZE_PTR(ce->unserialize_func);
+       SERIALIZE_PTR(ce->__isset);
+       SERIALIZE_PTR(ce->__unset);
+       SERIALIZE_PTR(ce->__tostring);
+       SERIALIZE_PTR(ce->__callstatic);
+       SERIALIZE_PTR(ce->__debugInfo);
+}
+
+static void zend_file_cache_serialize(zend_persistent_script   *script,
+                                      zend_file_cache_metainfo *info,
+                                      void                     *buf)
+{
+       zend_persistent_script *new_script;
+
+       memcpy(info->magic, "OPCACHE", 8);
+       memcpy(info->system_id, ZCG(system_id), 32);
+       info->mem_size = script->size;
+       info->str_size = 0;
+       info->script_offset = (char*)script - (char*)script->mem;
+       info->timestamp = script->timestamp;
+
+       memcpy(buf, script->mem, script->size);
+
+       new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
+       SERIALIZE_PTR(new_script->full_path);
+
+       zend_file_cache_serialize_hash(&new_script->class_table, script, info, buf, zend_file_cache_serialize_class);
+       zend_file_cache_serialize_hash(&new_script->function_table, script, info, buf, zend_file_cache_serialize_func);
+       zend_file_cache_serialize_op_array(&new_script->main_op_array, script, info, buf);
+
+       SERIALIZE_PTR(new_script->arena_mem);
+       new_script->mem = NULL;
+}
+
+int zend_file_cache_script_store(zend_persistent_script *script)
+{
+       size_t len;
+       int fd;
+       char *filename;
+       zend_file_cache_metainfo info;
+#ifndef ZEND_WIN32
+       struct iovec vec[3];
+#endif
+       void *mem, *buf;
+
+       len = strlen(ZCG(accel_directives).file_cache);
+       filename = emalloc(len + 33 + script->full_path->len + sizeof(SUFFIX));
+       memcpy(filename, ZCG(accel_directives).file_cache, len);
+       filename[len] = '/';
+       memcpy(filename + len + 1, ZCG(system_id), 32);
+       memcpy(filename + len + 33, script->full_path->val, script->full_path->len);
+       memcpy(filename + len + 33 + script->full_path->len, SUFFIX, sizeof(SUFFIX));
+
+       if (zend_file_cache_mkdir(filename, len) != SUCCESS) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
+               efree(filename);
+               return FAILURE;
+       }
+
+#ifndef ZEND_WIN32
+       fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+#else
+       fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, _S_IREAD | _S_IWRITE);
+#endif
+       if (fd < 0) {
+               if (errno != EEXIST) {
+                       zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s'\n", filename);
+               }
+               efree(filename);
+               return FAILURE;
+       }
+
+       if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
+               close(fd);
+               efree(filename);
+               return FAILURE;
+       }
+
+#ifdef __SSE2__
+       /* Align to 64-byte boundary */
+       mem = emalloc(script->size + 64);
+       buf = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
+#else
+       mem = buf = emalloc(script->size);
+#endif
+
+       ZCG(mem) = zend_string_alloc(4096 - (_STR_HEADER_SIZE + 1), 0);
+
+       zend_shared_alloc_init_xlat_table();
+       zend_file_cache_serialize(script, &info, buf);
+       zend_shared_alloc_destroy_xlat_table();
+
+       info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
+       info.checksum = zend_adler32(info.checksum, (signed char*)((zend_string*)ZCG(mem))->val, info.str_size);
+
+#ifndef ZEND_WIN32
+       vec[0].iov_base = &info;
+       vec[0].iov_len = sizeof(info);
+       vec[1].iov_base = buf;
+       vec[1].iov_len = script->size;
+       vec[2].iov_base = ((zend_string*)ZCG(mem))->val;
+       vec[2].iov_len = info.str_size;
+
+       if (writev(fd, vec, 3) != (ssize_t)(sizeof(info) + script->size + info.str_size)) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
+               zend_string_release((zend_string*)ZCG(mem));
+               efree(mem);
+               unlink(filename);
+               efree(filename);
+               return FAILURE;
+       }
+#else
+       if (ZEND_LONG_MAX < (zend_long)(sizeof(info) + script->size + info.str_size) ||
+               write(fd, &info, sizeof(info)) != sizeof(info) ||
+               write(fd, buf, script->size) != script->size ||
+               write(fd, ((zend_string*)ZCG(mem))->val, info.str_size) != info.str_size
+               ) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
+               zend_string_release((zend_string*)ZCG(mem));
+               efree(mem);
+               unlink(filename);
+               efree(filename);
+               return FAILURE;
+       }
+#endif
+
+       zend_string_release((zend_string*)ZCG(mem));
+       efree(mem);
+       if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+       }
+       close(fd);
+       efree(filename);
+
+       return SUCCESS;
+}
+
+static void zend_file_cache_unserialize_hash(HashTable               *ht,
+                                             zend_persistent_script  *script,
+                                             void                    *buf,
+                                             unserialize_callback_t   func)
+{
+       Bucket *p, *end;
+
+       if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
+               HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
+               return;
+       }
+       if (IS_UNSERIALIZED(ht->arData)) {
+               return;
+       }
+       UNSERIALIZE_PTR(ht->arData);
+       p = ht->arData;
+       end = p + ht->nNumUsed;
+       while (p < end) {
+               if (Z_TYPE(p->val) != IS_UNDEF) {
+                       UNSERIALIZE_PTR(p->key);
+                       func(&p->val, script, buf);
+               }
+               p++;
+       }
+}
+
+static zend_ast *zend_file_cache_unserialize_ast(zend_ast                *ast,
+                                                 zend_persistent_script  *script,
+                                                 void                    *buf)
+{
+       uint32_t i;
+
+       UNSERIALIZE_PTR(ast);
+
+       if (ast->kind == ZEND_AST_ZVAL) {
+               zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
+       } else if (zend_ast_is_list(ast)) {
+               zend_ast_list *list = zend_ast_get_list(ast);
+               for (i = 0; i < list->children; i++) {
+                       if (list->child[i]) {
+                               list->child[i] = zend_file_cache_unserialize_ast(list->child[i], script, buf);
+                       }
+               }
+       } else {
+               uint32_t children = zend_ast_get_num_children(ast);
+               for (i = 0; i < children; i++) {
+                       if (ast->child[i]) {
+                               ast->child[i] = zend_file_cache_unserialize_ast(ast->child[i], script, buf);
+                       }
+               }
+       }
+       return ast;
+}
+
+static void zend_file_cache_unserialize_zval(zval                    *zv,
+                                             zend_persistent_script  *script,
+                                             void                    *buf)
+{
+       switch (Z_TYPE_P(zv)) {
+               case IS_STRING:
+               case IS_CONSTANT:
+                       if (!IS_UNSERIALIZED(Z_STR_P(zv))) {
+                               UNSERIALIZE_PTR(Z_STR_P(zv));
+                       }
+                       break;
+               case IS_ARRAY:
+                       if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
+                               HashTable *ht;
+
+                               UNSERIALIZE_PTR(Z_ARR_P(zv));
+                               ht = Z_ARR_P(zv);
+                               zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_zval);
+                       }
+                       break;
+               case IS_REFERENCE:
+                       if (!IS_UNSERIALIZED(Z_REF_P(zv))) {
+                               zend_reference *ref;
+
+                               UNSERIALIZE_PTR(Z_REF_P(zv));
+                               ref = Z_REF_P(zv);
+                               zend_file_cache_unserialize_zval(&ref->val, script, buf);
+                       }
+                       break;
+               case IS_CONSTANT_AST:
+                       if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
+                               zend_ast_ref *ast;
+
+                               UNSERIALIZE_PTR(Z_AST_P(zv));
+                               ast = Z_AST_P(zv);
+                               if (!IS_UNSERIALIZED(ast->ast)) {
+                                       ast->ast = zend_file_cache_unserialize_ast(ast->ast, script, buf);
+                               }
+                       }
+                       break;
+       }
+}
+
+static void zend_file_cache_unserialize_op_array(zend_op_array           *op_array,
+                                                 zend_persistent_script  *script,
+                                                 void                    *buf)
+{
+       if (op_array->static_variables && !IS_UNSERIALIZED(op_array->static_variables)) {
+               HashTable *ht;
+
+               UNSERIALIZE_PTR(op_array->static_variables);
+               ht = op_array->static_variables;
+               zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_zval);
+       }
+
+       if (op_array->literals && !IS_UNSERIALIZED(op_array->literals)) {
+               zval *p, *end;
+
+               UNSERIALIZE_PTR(op_array->literals);
+               p = op_array->literals;
+               end = p + op_array->last_literal;
+               while (p < end) {
+                       zend_file_cache_unserialize_zval(p, script, buf);
+                       p++;
+               }
+       }
+
+       if (!IS_UNSERIALIZED(op_array->opcodes)) {
+               zend_op *opline, *end;
+
+               UNSERIALIZE_PTR(op_array->opcodes);
+               opline = op_array->opcodes;
+               end = opline + op_array->last;
+               while (opline < end) {
+# if ZEND_USE_ABS_CONST_ADDR
+                       if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                               UNSERIALIZE_PTR(opline->op1.zv);
+                       }
+                       if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                               UNSERIALIZE_PTR(opline->op2.zv);
+                       }
+# endif
+# if ZEND_USE_ABS_JMP_ADDR
+                       switch (opline->opcode) {
+                               case ZEND_JMP:
+                               case ZEND_GOTO:
+                               case ZEND_FAST_CALL:
+                                       UNSERIALIZE_PTR(opline->op1.jmp_addr);
+                                       break;
+                               case ZEND_JMPZNZ:
+                                       /* relative extended_value don't have to be changed */
+                                       /* break omitted intentionally */
+                               case ZEND_JMPZ:
+                               case ZEND_JMPNZ:
+                               case ZEND_JMPZ_EX:
+                               case ZEND_JMPNZ_EX:
+                               case ZEND_JMP_SET:
+                               case ZEND_COALESCE:
+                               case ZEND_NEW:
+                               case ZEND_FE_RESET_R:
+                               case ZEND_FE_RESET_RW:
+                               case ZEND_FE_FETCH_R:
+                               case ZEND_FE_FETCH_RW:
+                               case ZEND_ASSERT_CHECK:
+                                       UNSERIALIZE_PTR(opline->op2.jmp_addr);
+                                       break;
+                       }
+# endif
+                       ZEND_VM_SET_OPCODE_HANDLER(opline);
+                       opline++;
+               }
+
+               if (op_array->arg_info) {
+                       zend_arg_info *p, *end;
+                       UNSERIALIZE_PTR(op_array->arg_info);
+                       p = op_array->arg_info;
+                       end = p + op_array->num_args;
+                       if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+                               p--;
+                       }
+                       if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
+                               end++;
+                       }
+                       while (p < end) {
+                               if (!IS_UNSERIALIZED(p->name)) {
+                                       UNSERIALIZE_PTR(p->name);
+                               }
+                               if (!IS_UNSERIALIZED(p->class_name)) {
+                                       UNSERIALIZE_PTR(p->class_name);
+                               }
+                               p++;
+                       }
+               }
+
+               if (op_array->vars) {
+                       zend_string **p, **end;
+
+                       UNSERIALIZE_PTR(op_array->vars);
+                       p = op_array->vars;
+                       end = p + op_array->last_var;
+                       while (p < end) {
+                               if (!IS_UNSERIALIZED(*p)) {
+                                       UNSERIALIZE_PTR(*p);
+                               }
+                               p++;
+                       }
+               }
+
+               UNSERIALIZE_PTR(op_array->function_name);
+               UNSERIALIZE_PTR(op_array->filename);
+               UNSERIALIZE_PTR(op_array->brk_cont_array);
+               UNSERIALIZE_PTR(op_array->scope);
+               UNSERIALIZE_PTR(op_array->doc_comment);
+               UNSERIALIZE_PTR(op_array->try_catch_array);
+               UNSERIALIZE_PTR(op_array->prototype);
+       }
+}
+
+static void zend_file_cache_unserialize_func(zval                    *zv,
+                                             zend_persistent_script  *script,
+                                             void                    *buf)
+{
+       zend_op_array *op_array;
+
+       UNSERIALIZE_PTR(Z_PTR_P(zv));
+       op_array = Z_PTR_P(zv);
+       zend_file_cache_unserialize_op_array(op_array, script, buf);
+}
+
+static void zend_file_cache_unserialize_prop_info(zval                    *zv,
+                                                  zend_persistent_script  *script,
+                                                  void                    *buf)
+{
+       if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
+               zend_property_info *prop;
+
+               UNSERIALIZE_PTR(Z_PTR_P(zv));
+               prop = Z_PTR_P(zv);
+
+               if (prop->ce && !IS_UNSERIALIZED(prop->ce)) {
+                       UNSERIALIZE_PTR(prop->ce);
+               }
+               if (prop->name && !IS_UNSERIALIZED(prop->name)) {
+                       UNSERIALIZE_PTR(prop->name);
+               }
+               if (prop->doc_comment && !IS_UNSERIALIZED(prop->doc_comment)) {
+                       UNSERIALIZE_PTR(prop->doc_comment);
+               }
+       }
+}
+
+static void zend_file_cache_unserialize_class(zval                    *zv,
+                                              zend_persistent_script  *script,
+                                              void                    *buf)
+{
+       zend_class_entry *ce;
+
+       UNSERIALIZE_PTR(Z_PTR_P(zv));
+       ce = Z_PTR_P(zv);
+
+       UNSERIALIZE_PTR(ce->name);
+       zend_file_cache_unserialize_hash(&ce->function_table, script, buf, zend_file_cache_unserialize_func);
+       if (ce->default_properties_table) {
+               zval *p, *end;
+
+               UNSERIALIZE_PTR(ce->default_properties_table);
+               p = ce->default_properties_table;
+               end = p + ce->default_properties_count;
+               while (p < end) {
+                       zend_file_cache_unserialize_zval(p, script, buf);
+                       p++;
+               }
+       }
+       if (ce->default_static_members_table) {
+               zval *p, *end;
+
+               UNSERIALIZE_PTR(ce->default_static_members_table);
+               p = ce->default_static_members_table;
+               end = p + ce->default_static_members_count;
+               while (p < end) {
+                       zend_file_cache_unserialize_zval(p, script, buf);
+                       p++;
+               }
+       }
+       zend_file_cache_unserialize_hash(&ce->constants_table, script, buf, zend_file_cache_unserialize_zval);
+       UNSERIALIZE_PTR(ZEND_CE_FILENAME(ce));
+       UNSERIALIZE_PTR(ZEND_CE_DOC_COMMENT(ce));
+       zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info);
+
+       if (ce->trait_aliases) {
+               zend_trait_alias **p, *q;
+
+               UNSERIALIZE_PTR(ce->trait_aliases);
+               p = ce->trait_aliases;
+
+               while (*p) {
+                       UNSERIALIZE_PTR(*p);
+                       q = *p;
+
+                       if (q->trait_method) {
+                               zend_trait_method_reference *m;
+
+                               UNSERIALIZE_PTR(q->trait_method);
+                               m = q->trait_method;
+
+                               if (m->method_name) {
+                                       UNSERIALIZE_PTR(m->method_name);
+                               }
+                               if (m->class_name) {
+                                       UNSERIALIZE_PTR(m->class_name);
+                               }
+                       }
+
+                       if (q->alias) {
+                               UNSERIALIZE_PTR(q->alias);
+                       }
+                       p++;
+               }
+       }
+
+       if (ce->trait_precedences) {
+               zend_trait_precedence **p, *q;
+
+               UNSERIALIZE_PTR(ce->trait_precedences);
+               p = ce->trait_precedences;
+
+               while (*p) {
+                       UNSERIALIZE_PTR(*p);
+                       q = *p;
+
+                       if (q->trait_method) {
+                               zend_trait_method_reference *m;
+
+                               UNSERIALIZE_PTR(q->trait_method);
+                               m = q->trait_method;
+
+                               if (m->method_name) {
+                                       UNSERIALIZE_PTR(m->method_name);
+                               }
+                               if (m->class_name) {
+                                       UNSERIALIZE_PTR(m->class_name);
+                               }
+                       }
+
+                       if (q->exclude_from_classes) {
+                               zend_string **s;
+
+                               UNSERIALIZE_PTR(q->exclude_from_classes);
+                               s = (zend_string**)q->exclude_from_classes;
+
+                               while (*s) {
+                                       UNSERIALIZE_PTR(*s);
+                                       s++;
+                               }
+                       }
+                       p++;
+               }
+       }
+
+       UNSERIALIZE_PTR(ce->parent);
+       UNSERIALIZE_PTR(ce->constructor);
+       UNSERIALIZE_PTR(ce->destructor);
+       UNSERIALIZE_PTR(ce->clone);
+       UNSERIALIZE_PTR(ce->__get);
+       UNSERIALIZE_PTR(ce->__set);
+       UNSERIALIZE_PTR(ce->__call);
+       UNSERIALIZE_PTR(ce->serialize_func);
+       UNSERIALIZE_PTR(ce->unserialize_func);
+       UNSERIALIZE_PTR(ce->__isset);
+       UNSERIALIZE_PTR(ce->__unset);
+       UNSERIALIZE_PTR(ce->__tostring);
+       UNSERIALIZE_PTR(ce->__callstatic);
+       UNSERIALIZE_PTR(ce->__debugInfo);
+}
+
+static void zend_file_cache_unserialize(zend_persistent_script  *script,
+                                        void                    *buf)
+{
+       script->mem = buf;
+
+       UNSERIALIZE_PTR(script->full_path);
+
+       zend_file_cache_unserialize_hash(&script->class_table, script, buf, zend_file_cache_unserialize_class);
+       zend_file_cache_unserialize_hash(&script->function_table, script, buf, zend_file_cache_unserialize_func);
+       zend_file_cache_unserialize_op_array(&script->main_op_array, script, buf);
+
+       UNSERIALIZE_PTR(script->arena_mem);
+}
+
+zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
+{
+       zend_string *full_path = file_handle->opened_path;
+       size_t len;
+       int fd;
+       char *filename;
+       zend_persistent_script *script;
+       zend_file_cache_metainfo info;
+       zend_accel_hash_entry *bucket;
+       void *mem, *checkpoint, *buf;
+       int cache_it = 1;
+
+       if (!full_path) {
+               return NULL;
+       }
+       len = strlen(ZCG(accel_directives).file_cache);
+       filename = emalloc(len + 33 + full_path->len + sizeof(SUFFIX));
+       memcpy(filename, ZCG(accel_directives).file_cache, len);
+       filename[len] = '/';
+       memcpy(filename + len + 1, ZCG(system_id), 32);
+       memcpy(filename + len + 33, full_path->val, full_path->len);
+       memcpy(filename + len + 33 + full_path->len, SUFFIX, sizeof(SUFFIX));
+
+       fd = open(filename, O_RDONLY | O_BINARY);
+       if (fd < 0) {
+               efree(filename);
+               return NULL;
+       }
+
+       if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
+               close(fd);
+               efree(filename);
+               return NULL;
+       }
+
+       if (read(fd, &info, sizeof(info)) != sizeof(info)) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+               zend_file_cache_flock(fd, LOCK_UN);
+               close(fd);
+               unlink(filename);
+               efree(filename);
+               return NULL;
+       }
+
+       /* verify header */
+       if (memcmp(info.magic, "OPCACHE", 8) != 0 ||
+           memcmp(info.system_id, ZCG(system_id), 32) != 0) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+               zend_file_cache_flock(fd, LOCK_UN);
+               close(fd);
+               unlink(filename);
+               efree(filename);
+               return NULL;
+       }
+
+       /* verify timestamp */
+       if (ZCG(accel_directives).validate_timestamps &&
+           zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
+               if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+                       zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+               }
+               close(fd);
+               unlink(filename);
+               efree(filename);
+               return NULL;
+       }
+
+       checkpoint = zend_arena_checkpoint(CG(arena));
+#ifdef __SSE2__
+       /* Align to 64-byte boundary */
+       mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
+       mem = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
+#else
+       mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
+#endif
+
+       if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+               zend_file_cache_flock(fd, LOCK_UN);
+               close(fd);
+               unlink(filename);
+               zend_arena_release(&CG(arena), checkpoint);
+               efree(filename);
+               return NULL;
+       }
+       if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+               zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+       }
+       close(fd);
+
+       /* verify checksum */
+       if (ZCG(accel_directives).file_cache_consistency_checks &&
+           zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size) != info.checksum) {
+               zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s'\n", filename);
+               unlink(filename);
+               zend_arena_release(&CG(arena), checkpoint);
+               efree(filename);
+               return NULL;
+       }
+
+       if (!ZCG(accel_directives).file_cache_only) {
+               /* exclusive lock */
+               zend_shared_alloc_lock();
+
+               /* Check if we still need to put the file into the cache (may be it was
+                * already stored by another process. This final check is done under
+                * exclusive lock) */
+               bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
+               if (bucket) {
+                       script = (zend_persistent_script *)bucket->data;
+                       if (!script->corrupted) {
+                               zend_shared_alloc_unlock();
+                               zend_arena_release(&CG(arena), checkpoint);
+                               efree(filename);
+                               return script;
+                       }
+               }
+
+               if (zend_accel_hash_is_full(&ZCSG(hash))) {
+                       zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
+                       ZSMMG(memory_exhausted) = 1;
+                       zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
+                       zend_shared_alloc_unlock();
+                       goto use_process_mem;
+               }
+
+#ifdef __SSE2__
+               /* Align to 64-byte boundary */
+               buf = zend_shared_alloc(info.mem_size + 64);
+               buf = (void*)(((zend_uintptr_t)buf + 63L) & ~63L);
+#else
+               buf = zend_shared_alloc(info.mem_size);
+#endif
+
+               if (!buf) {
+                       zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
+                       zend_shared_alloc_unlock();
+                       goto use_process_mem;
+               }
+               memcpy(buf, mem, info.mem_size);
+       } else {
+use_process_mem:
+               buf = mem;
+               cache_it = 0;
+       }
+
+       ZCG(mem) = ((char*)mem + info.mem_size);
+       script = (zend_persistent_script*)((char*)buf + info.script_offset);
+       zend_file_cache_unserialize(script, buf);
+
+       if (cache_it) {
+               script->dynamic_members.checksum = zend_accel_script_checksum(script);
+
+               zend_accel_hash_update(&ZCSG(hash), script->full_path->val, script->full_path->len, 0, script);
+
+               zend_shared_alloc_unlock();
+               zend_arena_release(&CG(arena), checkpoint);
+       }
+       efree(filename);
+
+       return script;
+}
+
+void zend_file_cache_invalidate(zend_string *full_path)
+{
+       size_t len;
+       char *filename;
+
+       len = strlen(ZCG(accel_directives).file_cache);
+       filename = emalloc(len + 33 + full_path->len + sizeof(SUFFIX));
+       memcpy(filename, ZCG(accel_directives).file_cache, len);
+       filename[len] = '/';
+       memcpy(filename + len + 1, ZCG(system_id), 32);
+       memcpy(filename + len + 33, full_path->val, full_path->len);
+       memcpy(filename + len + 33 + full_path->len, SUFFIX, sizeof(SUFFIX));
+
+       unlink(filename);
+       efree(filename);
+}
+
+#endif /* HAVE_OPCACHE_FILE_CACHE */
diff --git a/ext/opcache/zend_file_cache.h b/ext/opcache/zend_file_cache.h
new file mode 100644 (file)
index 0000000..0660bfb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend OPcache                                                         |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2015 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:           |
+   | http://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: Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_FILE_CACHE_H
+#define ZEND_FILE_CACHE_H
+
+int zend_file_cache_script_store(zend_persistent_script *script);
+zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle);
+void zend_file_cache_invalidate(zend_string *full_path);
+
+#endif /* ZEND_FILE_CACHE_H */
index 5597d382e8a59e9b6df59b47d72fbee3b087acdd..4aee6fb628b3412084561ec8325bec9b5ab49545 100644 (file)
@@ -870,7 +870,7 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
        zend_shared_alloc_clear_xlat_table();
 
        zend_accel_store(script, sizeof(zend_persistent_script));
-       if (*key) {
+       if (key && *key) {
                *key = zend_accel_memdup(*key, key_length + 1);
        }
        zend_accel_store_string(script->full_path);
index d1925446fa7d7b568349fb8a2ad3b9c505169e00..19b51e7c59fa8f5090d66a57cb37abd2be11bcd8 100644 (file)
@@ -45,7 +45,6 @@
 /* True globals */
 /* old/new mapping. We can use true global even for ZTS because its usage
    is wrapped with exclusive lock anyway */
-static HashTable xlat_table;
 static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
 static const char *g_shared_model;
 /* pointer to globals allocated in SHM and shared across processes */
@@ -325,7 +324,7 @@ int zend_shared_memdup_size(void *source, size_t size)
 {
        void *old_p;
 
-       if ((old_p = zend_hash_index_find_ptr(&xlat_table, (zend_ulong)source)) != NULL) {
+       if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
                /* we already duplicated this pointer */
                return 0;
        }
@@ -337,7 +336,7 @@ void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source)
 {
        void *old_p, *retval;
 
-       if ((old_p = zend_hash_index_find_ptr(&xlat_table, (zend_ulong)source)) != NULL) {
+       if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
                /* we already duplicated this pointer */
                return old_p;
        }
@@ -393,21 +392,10 @@ void zend_shared_alloc_lock(void)
 #endif
 
        ZCG(locked) = 1;
-
-       /* Prepare translation table
-        *
-        * Make it persistent so that it uses malloc() and allocated blocks
-        * won't be taken from space which is freed by efree in memdup.
-        * Otherwise it leads to false matches in memdup check.
-        */
-       zend_hash_init(&xlat_table, 128, NULL, NULL, 1);
 }
 
 void zend_shared_alloc_unlock(void)
 {
-       /* Destroy translation table */
-       zend_hash_destroy(&xlat_table);
-
        ZCG(locked) = 0;
 
 #ifndef ZEND_WIN32
@@ -422,21 +410,39 @@ void zend_shared_alloc_unlock(void)
 #endif
 }
 
+void zend_shared_alloc_init_xlat_table(void)
+{
+
+       /* Prepare translation table
+        *
+        * Make it persistent so that it uses malloc() and allocated blocks
+        * won't be taken from space which is freed by efree in memdup.
+        * Otherwise it leads to false matches in memdup check.
+        */
+       zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 1);
+}
+
+void zend_shared_alloc_destroy_xlat_table(void)
+{
+       /* Destroy translation table */
+       zend_hash_destroy(&ZCG(xlat_table));
+}
+
 void zend_shared_alloc_clear_xlat_table(void)
 {
-       zend_hash_clean(&xlat_table);
+       zend_hash_clean(&ZCG(xlat_table));
 }
 
 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
 {
-       zend_hash_index_update_ptr(&xlat_table, (zend_ulong)old, (void*)new);
+       zend_hash_index_update_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new);
 }
 
 void *zend_shared_alloc_get_xlat_entry(const void *old)
 {
        void *retval;
 
-       if ((retval = zend_hash_index_find_ptr(&xlat_table, (zend_ulong)old)) == NULL) {
+       if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)old)) == NULL) {
                return NULL;
        }
        return retval;
index 1dbe6f8557923d2c3cbcea4b0f31859048106dcb..63947169b430b1e2a3684a6b0afeff4350cd3f49 100644 (file)
@@ -148,6 +148,8 @@ void zend_shared_alloc_unlock(void); /* returns the allocated size during lock..
 void zend_shared_alloc_safe_unlock(void);
 
 /* old/new mapping functions */
+void zend_shared_alloc_init_xlat_table(void);
+void zend_shared_alloc_destroy_xlat_table(void);
 void zend_shared_alloc_clear_xlat_table(void);
 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new);
 void *zend_shared_alloc_get_xlat_entry(const void *old);
index c03576cb2c99c718fd0d442e48ca17ca9e3638dd..c924d74c68c98d3189f121a179695e627c4dde73 100644 (file)
@@ -23,7 +23,7 @@ include $pname . '/a.php';
 
 if (function_exists("opcache_get_status")) {
        $status = opcache_get_status();
-       if ($status["opcache_enabled"]) {
+       if ($status["opcache_enabled"] || (isset($status["file_cache_only"]) && $status["file_cache_only"])) {
                ini_set("opcache.revalidate_freq", "0");
                sleep(2);
        }
index 905bfabc82840cc0f8fbbd3e7097035490ffe8ea..7d8bcb17da095b33af6d2634f36132a0268e58c9 100644 (file)
@@ -17,7 +17,7 @@ file_put_contents($pname . '/a.php', "brand new!\n");
 
 if (function_exists("opcache_get_status")) {
        $status = opcache_get_status();
-       if ($status["opcache_enabled"]) {
+       if ($status["opcache_enabled"] || (isset($status["file_cache_only"]) && $status["file_cache_only"])) {
                ini_set("opcache.revalidate_freq", "0");
                sleep(2);
        }
index 55d69cca0e29d1f46f6e0c6b0402ba6f0da53b29..c49ec513ff58847973e5fd8d32005ca19d446911 100644 (file)
@@ -17,7 +17,7 @@ file_put_contents($pname . '/a.php', "brand new!\n");
 
 if (function_exists("opcache_get_status")) {
        $status = opcache_get_status();
-       if ($status["opcache_enabled"]) {
+       if ($status["opcache_enabled"] || (isset($status["file_cache_only"]) && $status["file_cache_only"])) {
                ini_set("opcache.revalidate_freq", "0");
                sleep(2);
        }
index 72781e95596e6d9d9e3b0fbf4f8cafa876c2d7c1..897500a58b3561d275a2f2fb984f3d0b5f50e15d 100755 (executable)
@@ -243,9 +243,11 @@ $ini_overwrites = array(
                'opcache.file_update_protection=0',
        );
 
+$no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0';
+
 function write_information($show_html)
 {
-       global $cwd, $php, $php_cgi, $php_info, $user_tests, $ini_overwrites, $pass_options, $exts_to_test, $leak_check, $valgrind_header;
+       global $cwd, $php, $php_cgi, $php_info, $user_tests, $ini_overwrites, $pass_options, $exts_to_test, $leak_check, $valgrind_header, $no_file_cache;
 
        // Get info from php
        $info_file = __DIR__ . '/run-test-info.php';
@@ -261,11 +263,11 @@ More .INIs  : " , (function_exists(\'php_ini_scanned_files\') ? str_replace("\n"
        $info_params = array();
        settings2array($ini_overwrites, $info_params);
        settings2params($info_params);
-       $php_info = `$php $pass_options $info_params "$info_file"`;
+       $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`;
        define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`);
 
        if ($php_cgi && $php != $php_cgi) {
-               $php_info_cgi = `$php_cgi $pass_options $info_params -q "$info_file"`;
+               $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`;
                $php_info_sep = "\n---------------------------------------------------------------------";
                $php_cgi_info = "$php_info_sep\nPHP         : $php_cgi $php_info_cgi$php_info_sep";
        } else {
@@ -276,7 +278,7 @@ More .INIs  : " , (function_exists(\'php_ini_scanned_files\') ? str_replace("\n"
 
        // load list of enabled extensions
        save_text($info_file, '<?php echo join(",", get_loaded_extensions()); ?>');
-       $exts_to_test = explode(',',`$php $pass_options $info_params "$info_file"`);
+       $exts_to_test = explode(',',`$php $pass_options $info_params $no_file_cache "$info_file"`);
        // check for extensions that need special handling and regenerate
        $info_params_ex = array(
                'session' => array('session.auto_start=0'),
@@ -1195,6 +1197,7 @@ function run_test($php, $file, $env)
        global $valgrind_version;
        global $JUNIT;
        global $SHOW_ONLY_GROUPS;
+       global $no_file_cache;
        $temp_filenames = null;
        $org_file = $file;
 
@@ -1519,7 +1522,7 @@ TEST $file
 
                        junit_start_timer($shortname);
 
-                       $output = system_with_timeout("$extra $php $pass_options -q $ini_settings -d display_errors=0 \"$test_skipif\"", $env);
+                       $output = system_with_timeout("$extra $php $pass_options -q $ini_settings $no_file_cache -d display_errors=0 \"$test_skipif\"", $env);
 
                        junit_finish_timer($shortname);
 
@@ -1835,7 +1838,7 @@ COMMAND $cmd
                                settings2params($clean_params);
                                $extra = substr(PHP_OS, 0, 3) !== "WIN" ?
                                        "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": "";
-                               system_with_timeout("$extra $php $pass_options -q $clean_params \"$test_clean\"", $env);
+                               system_with_timeout("$extra $php $pass_options -q $clean_params $no_file_cache \"$test_clean\"", $env);
                        }
 
                        if (!$cfg['keep']['clean']) {