diff --git a/Zend/zend.c b/Zend/zend.c index f8d5dc1317356..b4f35130a243c 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -566,8 +566,13 @@ static void auto_global_dtor(zval *zv) /* {{{ */ static void function_copy_ctor(zval *zv) /* {{{ */ { zend_function *old_func = Z_FUNC_P(zv); - zend_function *func = pemalloc(sizeof(zend_internal_function), 1); + zend_function *func; + if (old_func->type == ZEND_USER_FUNCTION) { + ZEND_ASSERT(old_func->op_array.fn_flags & ZEND_ACC_IMMUTABLE); + return; + } + func = pemalloc(sizeof(zend_internal_function), 1); Z_FUNC_P(zv) = func; memcpy(func, old_func, sizeof(zend_internal_function)); function_add_ref(func); @@ -977,7 +982,9 @@ int zend_post_startup(void) /* {{{ */ zend_destroy_rsrc_list(&EG(persistent_list)); free(compiler_globals->function_table); + compiler_globals->function_table = NULL; free(compiler_globals->class_table); + compiler_globals->class_table = NULL; if ((script_encoding_list = (zend_encoding **)compiler_globals->script_encoding_list)) { compiler_globals_ctor(compiler_globals); compiler_globals->script_encoding_list = (const zend_encoding **)script_encoding_list; @@ -985,6 +992,7 @@ int zend_post_startup(void) /* {{{ */ compiler_globals_ctor(compiler_globals); } free(EG(zend_constants)); + EG(zend_constants) = NULL; executor_globals_ctor(executor_globals); global_persistent_list = &EG(persistent_list); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8b39a7a77f973..f7d3955887482 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -260,7 +260,7 @@ typedef struct _zend_oparray_context { /* User class has methods with static variables | | | */ #define ZEND_HAS_STATIC_IN_METHODS (1 << 15) /* X | | | */ /* | | | */ -/* Function Flags (unused: 26...30) | | | */ +/* Function Flags (unused: 27...30) | | | */ /* ============== | | | */ /* | | | */ /* deprecation flag | | | */ @@ -310,6 +310,9 @@ typedef struct _zend_oparray_context { /* internal function is allocated at arena (int only) | | | */ #define ZEND_ACC_ARENA_ALLOCATED (1 << 25) /* | X | | */ /* | | | */ +/* op_array is a clone of trait method | | | */ +#define ZEND_ACC_TRAIT_CLONE (1 << 26) /* | X | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1 << 31) /* | X | | */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index bd2e24d954d6e..646f3b927b94b 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -340,6 +340,23 @@ void shutdown_executor(void) /* {{{ */ destroy_op_array(&func->op_array); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + + /* Cleanup preloaded immutable functions */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_op_array *op_array = Z_PTR_P(zv); + if (op_array->type == ZEND_INTERNAL_FUNCTION) { + break; + } + ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_IMMUTABLE); + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + ZEND_ASSERT(GC_REFCOUNT(ht) == 1); + zend_array_destroy(ht); + } + } + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { if (_idx == EG(persistent_classes_count)) { break; @@ -347,6 +364,32 @@ void shutdown_executor(void) /* {{{ */ destroy_zend_class(zv); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + + /* Cleanup preloaded immutable classes */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE); + if (ce->default_static_members_count) { + zend_cleanup_internal_class_data(ce); + } + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_op_array *op_array; + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->type == ZEND_USER_FUNCTION) { + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + ZEND_ASSERT(GC_REFCOUNT(ht) == 1); + zend_array_destroy(ht); + } + } + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); } zend_cleanup_internal_classes(); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 00ae866054d4c..0ba108d18d659 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1308,6 +1308,7 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s } else { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_fn, fn, sizeof(zend_op_array)); + new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE; new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE; } function_add_ref(new_fn); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 3bfeb1f79c577..dbe2b5c20b520 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -362,7 +362,9 @@ void zend_class_add_ref(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); - ce->refcount++; + if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { + ce->refcount++; + } } ZEND_API void destroy_op_array(zend_op_array *op_array) diff --git a/Zend/zend_signal.c b/Zend/zend_signal.c index c4d4c8d3c94e4..6adca165d5f78 100644 --- a/Zend/zend_signal.c +++ b/Zend/zend_signal.c @@ -322,8 +322,10 @@ void zend_signal_activate(void) memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers)); - for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) { - zend_signal_register(zend_sigs[x], zend_signal_handler_defer); + if (SIGG(reset)) { + for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) { + zend_signal_register(zend_sigs[x], zend_signal_handler_defer); + } } SIGG(active) = 1; @@ -365,6 +367,7 @@ static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals) size_t x; memset(zend_signal_globals, 0, sizeof(*zend_signal_globals)); + zend_signal_globals->reset = 1; for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) { zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x]; diff --git a/Zend/zend_signal.h b/Zend/zend_signal.h index 54ca32e31bacc..c42a3991a53dd 100644 --- a/Zend/zend_signal.h +++ b/Zend/zend_signal.h @@ -59,6 +59,7 @@ typedef struct _zend_signal_globals_t { int running; /* in signal handler execution */ int active; /* internal signal handling is enabled */ zend_bool check; /* check for replaced handlers on shutdown */ + zend_bool reset; /* reset signal handlers on each request */ zend_signal_entry_t handlers[NSIG]; zend_signal_queue_t pstorage[ZEND_SIGNAL_QUEUE_SIZE], *phead, *ptail, *pavail; /* pending queue */ } zend_signal_globals_t; diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c index 1eae2ca22a065..dc3494a2bab30 100644 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -95,6 +95,8 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o { if (func->type == ZEND_USER_FUNCTION && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS)) + /* TODO: function copied from trait may be inconsistent ??? */ + && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE)) && fcall->extended_value >= func->op_array.required_num_args && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) { diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index 4dde298280411..a20180af3792a 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -53,6 +53,7 @@ static int zend_op_array_collect(zend_call_graph *call_graph, zend_op_array *op_ static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *script, zend_op_array_func_t func) { zend_class_entry *ce; + zend_string *key; zend_op_array *op_array; if (func(call_graph, &script->main_op_array) != SUCCESS) { @@ -65,9 +66,12 @@ static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *scrip } } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { if (func(call_graph, op_array) != SUCCESS) { return FAILURE; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 35843d19709af..5946d1ec8e48b 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -1421,6 +1421,7 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) { zend_class_entry *ce; + zend_string *key; zend_op_array *op_array; zend_string *name; zend_optimizer_ctx ctx; @@ -1440,9 +1441,12 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend zend_optimize_op_array(op_array, &ctx); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_optimize_op_array(op_array, &ctx); } } ZEND_HASH_FOREACH_END(); @@ -1544,16 +1548,22 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend zend_adjust_fcall_stack_size(op_array, &ctx); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_adjust_fcall_stack_size(op_array, &ctx); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); } - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) { zend_op_array *orig_op_array = @@ -1561,10 +1571,12 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend ZEND_ASSERT(orig_op_array != NULL); if (orig_op_array != op_array) { + uint32_t fn_flags = op_array->fn_flags; zend_function *prototype = op_array->prototype; HashTable *ht = op_array->static_variables; *op_array = *orig_op_array; + op_array->fn_flags = fn_flags; op_array->prototype = prototype; op_array->static_variables = ht; } @@ -1582,7 +1594,7 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); } } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 1a7e617d427b5..8b45140f5f1bc 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -31,6 +31,8 @@ #include "zend_accelerator_blacklist.h" #include "zend_list.h" #include "zend_execute.h" +#include "zend_inheritance.h" +#include "main/php_main.h" #include "main/SAPI.h" #include "main/php_streams.h" #include "main/php_open_temporary_file.h" @@ -128,6 +130,11 @@ static int (*orig_post_startup_cb)(void); static void accel_gen_system_id(void); static int accel_post_startup(void); +static int accel_finish_startup(void); + +static void preload_shutdown(void); +static void preload_activate(void); +static void preload_restart(void); #ifdef ZEND_WIN32 # define INCREMENT(v) InterlockedIncrement64(&ZCSG(v)) @@ -1073,7 +1080,9 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle) { - if (ZCG(accel_directives).revalidate_freq && + if (persistent_script->timestamp == 0) { + return SUCCESS; /* Don't check timestamps of preloaded scripts */ + } else if (ZCG(accel_directives).revalidate_freq && persistent_script->dynamic_members.revalidate >= ZCG(request_time)) { return SUCCESS; } else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) { @@ -2373,6 +2382,9 @@ int accel_activate(INIT_FUNC_ARGS) } zend_shared_alloc_restore_state(); + if (ZCSG(preload_script)) { + preload_restart(); + } ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart); if (ZCSG(last_restart_time) < ZCG(request_time)) { ZCSG(last_restart_time) = ZCG(request_time); @@ -2402,6 +2414,10 @@ int accel_activate(INIT_FUNC_ARGS) accel_reset_pcre_cache(); } + if (ZCSG(preload_script)) { + preload_activate(); + } + return SUCCESS; } @@ -2899,7 +2915,7 @@ static int accel_post_startup(void) accel_use_shm_interned_strings(); } - return SUCCESS; + return accel_finish_startup(); } static void (*orig_post_shutdown_cb)(void); @@ -2925,6 +2941,10 @@ void accel_shutdown(void) return; } + if (ZCSG(preload_script)) { + preload_shutdown(); + } + #ifdef HAVE_OPCACHE_FILE_CACHE _file_cache_only = file_cache_only; #endif @@ -3022,6 +3042,941 @@ void accelerator_shm_read_unlock(void) } } +/* Preloading */ +static HashTable *preload_scripts = NULL; +static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type); + +static void preload_shutdown(void) +{ + zval *zv; + +#if 0 + if (EG(zend_constants)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(zend_constants), zv) { + zend_constant *c = Z_PTR_P(zv); + if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } +#endif + + if (EG(function_table)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_function *func = Z_PTR_P(zv); + if (func->type == ZEND_INTERNAL_FUNCTION) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } + + if (EG(class_table)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } +} + +static void preload_activate(void) +{ + if (ZCSG(preload_script)->ping_auto_globals_mask) { + zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask); + } +} + +static void preload_restart(void) +{ + zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(ZCSG(preload_script)->script.filename), ZSTR_LEN(ZCSG(preload_script)->script.filename), 0, ZCSG(preload_script)); + if (ZCSG(saved_scripts)) { + zend_persistent_script **p = ZCSG(saved_scripts); + while (*p) { + zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL((*p)->script.filename), ZSTR_LEN((*p)->script.filename), 0, *p); + p++; + } + } +} + +static void preload_move_user_functions(HashTable *src, HashTable *dst) +{ + Bucket *p; + dtor_func_t orig_dtor = src->pDestructor; + + src->pDestructor = NULL; + zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); + ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { + zend_function *function = Z_PTR(p->val); + + if (EXPECTED(function->type == ZEND_USER_FUNCTION)) { + _zend_hash_append_ptr(dst, p->key, function); + zend_hash_del_bucket(src, p); + } else { + break; + } + } ZEND_HASH_FOREACH_END(); + src->pDestructor = orig_dtor; +} + +static void preload_move_user_classes(HashTable *src, HashTable *dst) +{ + Bucket *p; + dtor_func_t orig_dtor = src->pDestructor; + + src->pDestructor = NULL; + zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); + ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { + zend_class_entry *ce = Z_PTR(p->val); + + if (EXPECTED(ce->type == ZEND_USER_CLASS)) { + _zend_hash_append_ptr(dst, p->key, ce); + zend_hash_del_bucket(src, p); + } else { + break; + } + } ZEND_HASH_FOREACH_END(); + src->pDestructor = orig_dtor; +} + +static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type) +{ + zend_op_array *op_array = preload_orig_compile_file(file_handle, type); + + if (op_array && op_array->refcount) { + zend_persistent_script *script; + + script = create_persistent_script(); + script->script.first_early_binding_opline = (uint32_t)-1; + script->script.filename = zend_string_copy(op_array->filename); + zend_string_hash_val(script->script.filename); + script->script.main_op_array = *op_array; + +//??? efree(op_array->refcount); + op_array->refcount = NULL; + + if (op_array->static_variables && + !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(op_array->static_variables); + } + + zend_hash_add_ptr(preload_scripts, script->script.filename, script); + } + + return op_array; +} + +static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp) /* {{{ */ +{ + Bucket *b1 = base; + Bucket *b2; + Bucket *end = b1 + count; + Bucket tmp; + zend_class_entry *ce, *p; + + while (b1 < end) { +try_again: + ce = (zend_class_entry*)Z_PTR(b1->val); + if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { + p = ce->parent; + if (p->type == ZEND_USER_CLASS) { + b2 = b1 + 1; + while (b2 < end) { + if (p == Z_PTR(b2->val)) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } + b2++; + } + } + } + if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) { + uint32_t i = 0; + for (i = 0; i < ce->num_interfaces; i++) { + p = ce->interfaces[i]; + if (p->type == ZEND_USER_CLASS) { + b2 = b1 + 1; + while (b2 < end) { + if (p == Z_PTR(b2->val)) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } + b2++; + } + } + } + } + b1++; + } +} + +static void preload_link(void) +{ + zval *zv; + zend_persistent_script *script; + zend_class_entry *ce, *parent, *p; + zend_string *key; + zend_bool found, changed; + uint32_t i; + zend_op_array *op_array; + dtor_func_t orig_dtor; + + /* Resolve class dependencies */ + do { + changed = 0; + + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if ((ce->ce_flags & ZEND_ACC_TOP_LEVEL) + && !(ce->ce_flags & ZEND_ACC_LINKED)) { + + parent = NULL; + + if (ce->parent_name) { + key = zend_string_tolower(ce->parent_name); + parent = zend_hash_find_ptr(EG(class_table), key); + zend_string_release(key); + if (!parent) continue; +#ifdef ZEND_WIN32 + /* On Windows we can't link with internal class, because of ASLR */ + if (parent->type == ZEND_INTERNAL_CLASS) continue; +#endif + } + + if (ce->num_interfaces) { + found = 1; + for (i = 0; i < ce->num_interfaces; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); + if (!p) { + found = 0; + break; + } +#ifdef ZEND_WIN32 + /* On Windows we can't link with internal class, because of ASLR */ + if (p->type == ZEND_INTERNAL_CLASS) { + found = 0; + break; + } +#endif + } + if (!found) continue; + } + + if (ce->num_traits) { + found = 1; + for (i = 0; i < ce->num_traits; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); + if (!p) { + found = 0; + break; + } +#ifdef ZEND_WIN32 + /* On Windows we can't link with internal class, because of ASLR */ + if (p->type == ZEND_INTERNAL_CLASS) { + found = 0; + break; + } +#endif + } + if (!found) continue; + } + + key = zend_string_tolower(ce->name); + zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key); + zend_string_release(key); + if (EXPECTED(zv)) { + zend_do_link_class(ce, parent); + changed = 1; + } + } + } ZEND_HASH_FOREACH_END(); + } while (changed); + + /* Resolve class constants */ + EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */ + do { + changed = 0; + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if ((ce->ce_flags & ZEND_ACC_LINKED) + && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + zend_bool ok = 1; + zend_class_constant *c; + zval *val; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + val = &c->value; + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) { + changed = 1; + } else { + ok = 0; + } + } + } ZEND_HASH_FOREACH_END(); + if (ce->default_properties_count) { + zend_class_entry *pce = ce; + + val = ce->default_properties_table + ce->default_properties_count - 1; + do { + uint32_t count = pce->parent ? pce->default_properties_count - pce->parent->default_properties_count : pce->default_properties_count; + + while (count) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(val, pce) != SUCCESS)) { + ok = 0; + } + } + val--; + count--; + } + pce = pce->parent; + } while (pce && pce-> default_properties_count); + } + if (ce->default_static_members_count) { + uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count; + + val = ce->default_static_members_table + ce->default_static_members_count - 1; + while (count) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) { + ok = 0; + } + } + val--; + count--; + } + } + if (ok) { + ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + } + } + } ZEND_HASH_FOREACH_END(); + } while (changed); + EG(exception) = NULL; + + /* Move unlinked clases (and with unresilved constants) back to scripts */ + orig_dtor = EG(class_table)->pDestructor; + EG(class_table)->pDestructor = NULL; + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + zend_error(E_WARNING, "Can't preload unlinked class %s at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start); + } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + zend_error(E_WARNING, "Can't preload class %s with unresolved constants at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start); + } else { + continue; + } + script = zend_hash_find_ptr(preload_scripts, ce->info.user.filename); + ZEND_ASSERT(script); + zend_hash_add(&script->script.class_table, key, zv); + ZVAL_UNDEF(zv); + zend_string_release(key); + EG(class_table)->nNumOfElements--; + } ZEND_HASH_FOREACH_END(); + EG(class_table)->pDestructor = orig_dtor; + zend_hash_rehash(EG(class_table)); + + /* Move run-time declared functions back to scripts */ + orig_dtor = EG(function_table)->pDestructor; + EG(function_table)->pDestructor = NULL; + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(function_table), key, zv) { + op_array = Z_PTR_P(zv); + if (op_array->type == ZEND_INTERNAL_FUNCTION) { + break; + } + if (op_array->fn_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_CLOSURE)) { + continue; + } + script = zend_hash_find_ptr(preload_scripts, op_array->filename); + ZEND_ASSERT(script); + zend_hash_add(&script->script.function_table, key, zv); + ZVAL_UNDEF(zv); + zend_string_release(key); + EG(function_table)->nNumOfElements--; + } ZEND_HASH_FOREACH_END(); + EG(function_table)->pDestructor = orig_dtor; + zend_hash_rehash(EG(function_table)); + + + /* Remove DECLARE opcodes */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + zend_op_array *op_array = &script->script.main_op_array; + zend_op *opline = op_array->opcodes; + zend_op *end = opline + op_array->last; + + while (opline != end) { + switch (opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_INHERITED_CLASS: + case ZEND_DECLARE_INHERITED_CLASS_DELAYED: + key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1); + zv = zend_hash_find_ex(EG(class_table), key, 1); + if (EXPECTED(!zv)) { + MAKE_NOP(opline); + } + break; + } + opline++; + } + + if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { + script->script.first_early_binding_opline = zend_build_delayed_early_binding_list(op_array); + if (script->script.first_early_binding_opline == (uint32_t)-1) { + op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING; + } + } + } ZEND_HASH_FOREACH_END(); +} + +static zend_string *preload_resolve_path(zend_string *filename) +{ + return zend_resolve_path(ZSTR_VAL(filename), ZSTR_LEN(filename)); +} + +static void preload_remove_empty_includes(void) +{ + zend_persistent_script *script; + zend_bool changed; + + /* mark all as empty */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + script->empty = 1; + } ZEND_HASH_FOREACH_END(); + + /* find non empty scripts */ + do { + changed = 0; + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + if (script->empty) { + int empty = 1; + zend_op *opline = script->script.main_op_array.opcodes; + zend_op *end = opline + script->script.main_op_array.last; + + while (opline < end) { + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + opline->extended_value != ZEND_EVAL && + opline->op1_type == IS_CONST && + Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) { + + zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + + if (resolved_path) { + zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path); + zend_string_release(resolved_path); + if (!incl->empty) { + empty = 0; + break; + } + } else { + empty = 0; + break; + } + } else if (opline->opcode != ZEND_NOP && + opline->opcode != ZEND_RETURN && + opline->opcode != ZEND_HANDLE_EXCEPTION) { + empty = 0; + break; + } + opline++; + } + if (!empty) { + script->empty = 0; + changed = 1; + } + } + } ZEND_HASH_FOREACH_END(); + } while (changed); + + /* remove empty includes */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + zend_op *opline = script->script.main_op_array.opcodes; + zend_op *end = opline + script->script.main_op_array.last; + + while (opline < end) { + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + opline->extended_value != ZEND_EVAL && + opline->op1_type == IS_CONST && + Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) { + + zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + + if (resolved_path) { + zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path); + if (incl->empty) { + MAKE_NOP(opline); + } else { + if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) { + /* replace relative patch with absolute one */ + zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path); + } + } + zend_string_release(resolved_path); + } + } + opline++; + } + } ZEND_HASH_FOREACH_END(); +} + +static int preload_optimize(zend_persistent_script *script) +{ + zend_class_entry *ce; + zend_op_array *op_array; + + zend_shared_alloc_init_xlat_table(); + + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_TRAIT) { + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array); + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); + + if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) { + return FAILURE; + } + + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS) { + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) { + zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + if (orig_op_array) { + zend_class_entry *scope = op_array->scope; + uint32_t fn_flags = op_array->fn_flags; + zend_function *prototype = op_array->prototype; + HashTable *ht = op_array->static_variables; + *op_array = *orig_op_array; + op_array->scope = scope; + op_array->fn_flags = fn_flags; + op_array->prototype = prototype; + op_array->static_variables = ht; + } + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); + + zend_shared_alloc_destroy_xlat_table(); + + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) { + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + return SUCCESS; +} + +static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script) +{ + zend_accel_hash_entry *bucket; + uint32_t memory_used; + uint32_t checkpoint; + + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading!"); + return NULL; + } + + checkpoint = zend_shared_alloc_checkpoint_xlat_table(); + + /* Calculate the required memory size */ + memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0, 1); + + /* Allocate shared memory */ +#if defined(__AVX__) || defined(__SSE2__) + /* Align to 64-byte boundary */ + ZCG(mem) = zend_shared_alloc(memory_used + 64); + if (ZCG(mem)) { + ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L); +#if defined(__x86_64__) + memset(ZCG(mem), 0, memory_used); +#elif defined(__AVX__) + { + char *p = (char*)ZCG(mem); + char *end = p + memory_used; + __m256i ymm0 = _mm256_setzero_si256(); + + while (p < end) { + _mm256_store_si256((__m256i*)p, ymm0); + _mm256_store_si256((__m256i*)(p+32), ymm0); + p += 64; + } + } +#else + { + char *p = (char*)ZCG(mem); + char *end = p + memory_used; + __m128i xmm0 = _mm_setzero_si128(); + + while (p < end) { + _mm_store_si128((__m128i*)p, xmm0); + _mm_store_si128((__m128i*)(p+16), xmm0); + _mm_store_si128((__m128i*)(p+32), xmm0); + _mm_store_si128((__m128i*)(p+48), xmm0); + p += 64; + } + } +#endif + } +#else + ZCG(mem) = zend_shared_alloc(memory_used); + if (ZCG(mem)) { + memset(ZCG(mem), 0, memory_used); + } +#endif + if (!ZCG(mem)) { + zend_accel_error(ACCEL_LOG_FATAL, "Not enough shared memory for preloading!"); + return NULL; + } + + zend_shared_alloc_restore_xlat_table(checkpoint); + + /* Copy into shared memory */ + new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 1); + + new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename); + + /* 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=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n", + ZSTR_VAL(new_persistent_script->script.filename), + (size_t)new_persistent_script->mem, + (size_t)((char *)new_persistent_script->mem + new_persistent_script->size), + (size_t)ZCG(mem)); + } + + new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script); + + /* store script structure in the hash table */ + bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script); + if (bucket) { + zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename)); + } + + new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size); + + return new_persistent_script; +} + +static void preload_load(void) +{ + /* Load into process tables */ + if (zend_hash_num_elements(&ZCSG(preload_script)->script.function_table)) { + Bucket *p = ZCSG(preload_script)->script.function_table.arData; + Bucket *end = p + ZCSG(preload_script)->script.function_table.nNumUsed; + + for (; p != end; p++) { + _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1); + } + } + + if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) { + Bucket *p = ZCSG(preload_script)->script.class_table.arData; + Bucket *end = p + ZCSG(preload_script)->script.class_table.nNumUsed; + + for (; p != end; p++) { + _zend_hash_append_ptr_ex(CG(class_table), p->key, Z_PTR(p->val), 1); + } + } + + if (EG(zend_constants)) { + EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; + } + if (EG(function_table)) { + EG(persistent_functions_count) = EG(function_table)->nNumUsed; + } + if (EG(class_table)) { + EG(persistent_classes_count) = EG(class_table)->nNumUsed; + } + CG(map_ptr_last) = ZCSG(map_ptr_last); +} + +static int accel_preload(const char *config) +{ + zend_file_handle file_handle; + int ret; + uint32_t orig_compiler_options; + + ZCG(enabled) = 0; + preload_orig_compile_file = accelerator_orig_compile_file; + accelerator_orig_compile_file = preload_compile_file; + + orig_compiler_options = CG(compiler_options); + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; +// CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; +// CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; + + /* Compile and execute proloading script */ + memset(&file_handle, 0, sizeof(file_handle)); + file_handle.filename = (char*)config; + file_handle.free_filename = 0; + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.opened_path = NULL; + file_handle.handle.fp = NULL; + + preload_scripts = emalloc(sizeof(HashTable)); + zend_hash_init(preload_scripts, 0, NULL, NULL, 0); + + zend_try { + ret = zend_execute_scripts(ZEND_REQUIRE, NULL, 1, &file_handle); + } zend_catch { + ret = FAILURE; + } zend_end_try(); + + CG(compiler_options) = orig_compiler_options; + accelerator_orig_compile_file = preload_orig_compile_file; + ZCG(enabled) = 1; + + zend_destroy_file_handle(&file_handle); + + if (ret == SUCCESS) { + zend_persistent_script *script; + zend_string *filename; + int i; + + /* Release stored values to avoid dangling pointers */ + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0); + + preload_link(); + preload_remove_empty_includes(); + + /* Don't preload constants */ + if (EG(zend_constants)) { + zval *zv; + ZEND_HASH_REVERSE_FOREACH_VAL(EG(zend_constants), zv) { + zend_constant *c = Z_PTR_P(zv); + if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) { + break; + } + EG(zend_constants)->pDestructor(zv); + } ZEND_HASH_FOREACH_END_DEL(); + } + + script = create_persistent_script(); + + /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we + will have to ping the used auto global variables before execution */ + if (PG(auto_globals_jit)) { + script->ping_auto_globals_mask = zend_accel_get_auto_globals(); + } else { + script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit(); + } + + /* Store all functions and classes in a single pseudo-file */ + filename = zend_string_init("$PRELOAD$", strlen("$PRELOAD$"), 0); +#if ZEND_USE_ABS_CONST_ADDR + init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1); +#else + init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2); +#endif + script->script.main_op_array.last = 1; + script->script.main_op_array.last_literal = 1; +#if ZEND_USE_ABS_CONST_ADDR + script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval)); +#else + script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1); +#endif + ZVAL_NULL(script->script.main_op_array.literals); + memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op)); + script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN; + script->script.main_op_array.opcodes[0].op1_type = IS_CONST; + script->script.main_op_array.opcodes[0].op1.constant = 0; + ZEND_PASS_TWO_UPDATE_CONSTANT(&script->script.main_op_array, script->script.main_op_array.opcodes, script->script.main_op_array.opcodes[0].op1); + + script->script.main_op_array.filename = filename; + script->script.filename = zend_string_copy(filename); + + script->script.first_early_binding_opline = (uint32_t)-1; + + preload_move_user_functions(CG(function_table), &script->script.function_table); + preload_move_user_classes(CG(class_table), &script->script.class_table); + + zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0); + + if (preload_optimize(script) != SUCCESS) { + zend_accel_error(ACCEL_LOG_FATAL, "Optimization error during preloading!"); + return FAILURE; + } + + zend_shared_alloc_init_xlat_table(); + + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + ZCSG(preload_script) = preload_script_in_shared_memory(script); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + zend_string_release(filename); + + ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0); + + preload_load(); + + /* Store individual scripts with unlinked classes */ + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + i = 0; + ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*)); + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script); + } ZEND_HASH_FOREACH_END(); + ZCSG(saved_scripts)[i] = NULL; + + zend_shared_alloc_save_state(); + accel_interned_strings_save_state(); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + zend_shared_alloc_destroy_xlat_table(); + } + + zend_hash_destroy(preload_scripts); + efree(preload_scripts); + preload_scripts = NULL; + + return ret; +} + +size_t preload_ub_write(const char *str, size_t str_length) +{ + return fwrite(str, 1, str_length, stdout); +} + +void preload_flush(void *server_context) +{ + fflush(stdout); +} + +static int accel_finish_startup(void) +{ + if (!ZCG(enabled) || !accel_startup_ok) { + return SUCCESS; + } + + if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) { + int ret = SUCCESS; + + int (*orig_activate)(TSRMLS_D) = sapi_module.activate; + int (*orig_deactivate)(TSRMLS_D) = sapi_module.deactivate; + void (*orig_register_server_variables)(zval *track_vars_array TSRMLS_DC) = sapi_module.register_server_variables; + int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.header_handler; + int (*orig_send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.send_headers; + void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)= sapi_module.send_header; + char *(*orig_getenv)(char *name, size_t name_len TSRMLS_DC) = sapi_module.getenv; + size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; + void (*orig_flush)(void *server_context) = sapi_module.flush; +#ifdef ZEND_SIGNALS + zend_bool old_reset_signals = SIGG(reset); +#endif + + if (UNEXPECTED(file_cache_only)) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); + return SUCCESS; + } + + /* exclusive lock */ + zend_shared_alloc_lock(); + + if (ZCSG(preload_script)) { + /* Preloading was done in another process */ + preload_load(); + zend_shared_alloc_unlock(); + return SUCCESS; + } + + sapi_module.activate = NULL; + sapi_module.deactivate = NULL; + sapi_module.register_server_variables = NULL; + sapi_module.header_handler = NULL; + sapi_module.send_headers = NULL; + sapi_module.send_header = NULL; + sapi_module.getenv = NULL; + sapi_module.ub_write = preload_ub_write; + sapi_module.flush = preload_flush; + + zend_interned_strings_switch_storage(1); + +#ifdef ZEND_SIGNALS + SIGG(reset) = 0; +#endif + if (php_request_startup() == SUCCESS) { + + /* don't send headers */ + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + php_output_set_status(0); + + ZCG(auto_globals_mask) = 0; + ZCG(request_time) = (time_t)sapi_get_request_time(); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_key_len) = 0; + ZCG(include_path_check) = 1; + + ZCG(cwd) = NULL; + ZCG(cwd_key_len) = 0; + ZCG(cwd_check) = 1; + + if (accel_preload(ZCG(accel_directives).preload) != SUCCESS) { + ret = FAILURE; + } + + php_request_shutdown(NULL); + } else { + ret = FAILURE; + } +#ifdef ZEND_SIGNALS + SIGG(reset) = old_reset_signals; +#endif + + sapi_module.activate = orig_activate; + sapi_module.deactivate = orig_deactivate; + sapi_module.register_server_variables = orig_register_server_variables; + sapi_module.header_handler = orig_header_handler; + sapi_module.send_headers = orig_send_headers; + sapi_module.send_header = orig_send_header; + sapi_module.getenv = orig_getenv; + sapi_module.ub_write = orig_ub_write; + sapi_module.flush = orig_flush; + + zend_shared_alloc_unlock(); + + sapi_activate(); + + return ret; + } + + return SUCCESS; +} + ZEND_EXT_API zend_extension zend_extension_entry = { ACCELERATOR_PRODUCT_NAME, /* name */ PHP_VERSION, /* version */ diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index e9fae11984dca..2313666725ad6 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -143,6 +143,7 @@ typedef struct _zend_persistent_script { accel_time_t timestamp; /* the script modification time */ zend_bool corrupted; zend_bool is_phar; + zend_bool empty; void *mem; /* shared memory area used by script structures */ size_t size; /* size of used shared memory */ @@ -212,6 +213,7 @@ typedef struct _zend_accel_directives { #ifdef HAVE_HUGE_CODE_PAGES zend_bool huge_code_pages; #endif + char *preload; } zend_accel_directives; typedef struct _zend_accel_globals { @@ -284,6 +286,10 @@ typedef struct _zend_accel_shared_globals { #endif zend_bool restart_in_progress; + /* Preloading */ + zend_persistent_script *preload_script; + zend_persistent_script **saved_scripts; + /* uninitialized HashTable Support */ uint32_t uninitialized_bucket[-HT_MIN_MASK]; diff --git a/ext/opcache/tests/preload.inc b/ext/opcache/tests/preload.inc new file mode 100644 index 0000000000000..af20c4947f8a7 --- /dev/null +++ b/ext/opcache/tests/preload.inc @@ -0,0 +1,39 @@ + +--FILE-- + +OK +--EXPECTF-- +bool(true) +bool(false) +bool(true) +OK diff --git a/ext/opcache/tests/preload_002.phpt b/ext/opcache/tests/preload_002.phpt new file mode 100644 index 0000000000000..38c3e8af685a7 --- /dev/null +++ b/ext/opcache/tests/preload_002.phpt @@ -0,0 +1,19 @@ +--TEST-- +Preloading prototypes +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- + +--FILE-- +getPrototype()->class); +vaR_dump((new ReflectionMethod('x', 'bar'))->getPrototype()->class); +?> +OK +--EXPECT-- +string(1) "b" +string(1) "a" +OK diff --git a/ext/opcache/tests/preload_003.phpt b/ext/opcache/tests/preload_003.phpt new file mode 100644 index 0000000000000..b8083879670ab --- /dev/null +++ b/ext/opcache/tests/preload_003.phpt @@ -0,0 +1,19 @@ +--TEST-- +Preloading classes linked with traits +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- + +--FILE-- + +OK +--EXPECT-- +string(7) "T1::foo" +string(7) "T2::bar" +OK diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 8f89309f13f32..7a1f46653c07b 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -323,6 +323,7 @@ ZEND_INI_BEGIN() #ifdef HAVE_HUGE_CODE_PAGES STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals) #endif + STD_PHP_INI_ENTRY("opcache.preload" , "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.preload, zend_accel_globals, accel_globals) ZEND_INI_END() static int filename_is_in_cache(zend_string *filename) @@ -661,6 +662,49 @@ static ZEND_FUNCTION(opcache_get_status) add_assoc_double(&statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0); add_assoc_zval(return_value, "opcache_statistics", &statistics); + if (ZCSG(preload_script)) { + array_init(&statistics); + + add_assoc_long(&statistics, "memory_consumption", ZCSG(preload_script)->dynamic_members.memory_consumption); + + if (zend_hash_num_elements(&ZCSG(preload_script)->script.function_table)) { + zend_op_array *op_array; + + array_init(&scripts); + ZEND_HASH_FOREACH_PTR(&ZCSG(preload_script)->script.function_table, op_array) { + add_next_index_str(&scripts, op_array->function_name); + } ZEND_HASH_FOREACH_END(); + add_assoc_zval(&statistics, "functions", &scripts); + } + + if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) { + zend_class_entry *ce; + zend_string *key; + + array_init(&scripts); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ZCSG(preload_script)->script.class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + add_next_index_str(&scripts, key); + } else { + add_next_index_str(&scripts, ce->name); + } + } ZEND_HASH_FOREACH_END(); + add_assoc_zval(&statistics, "classes", &scripts); + } + + if (ZCSG(saved_scripts)) { + zend_persistent_script **p = ZCSG(saved_scripts); + + array_init(&scripts); + while (*p) { + add_next_index_str(&scripts, (*p)->script.filename); + p++; + } + add_assoc_zval(&statistics, "scripts", &scripts); + } + add_assoc_zval(return_value, "preload_statistics", &statistics); + } + if (fetch_scripts) { /* accelerated scripts */ if (accelerator_get_scripts(&scripts)) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index f79020b8aae7b..1f7cccca02374 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -718,6 +718,7 @@ static void zend_persist_class_method(zval *zv) static void zend_persist_property_info(zval *zv) { zend_property_info *prop = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + zend_class_entry *ce; if (prop) { Z_PTR_P(zv) = prop; @@ -728,7 +729,10 @@ static void zend_persist_property_info(zval *zv) } else { prop = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_property_info)); } - prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); + ce = zend_shared_alloc_get_xlat_entry(prop->ce); + if (ce) { + prop->ce = ce; + } zend_accel_store_interned_string(prop->name); if (prop->doc_comment) { if (ZCG(accel_directives).save_comments) { @@ -746,6 +750,7 @@ static void zend_persist_property_info(zval *zv) static void zend_persist_class_constant(zval *zv) { zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + zend_class_entry *ce; if (c) { Z_PTR_P(zv) = c; @@ -757,7 +762,10 @@ static void zend_persist_class_constant(zval *zv) c = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_class_constant)); } zend_persist_zval(&c->value); - c->ce = zend_shared_alloc_get_xlat_entry(c->ce); + ce = zend_shared_alloc_get_xlat_entry(c->ce); + if (ce) { + c->ce = ce; + } if (c->doc_comment) { if (ZCG(accel_directives).save_comments) { zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); @@ -981,43 +989,82 @@ static int zend_update_parent_ce(zval *zv) /* update methods */ if (ce->constructor) { - ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor); + if (tmp != NULL) { + ce->constructor = tmp; + } } if (ce->destructor) { - ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->destructor); + if (tmp != NULL) { + ce->destructor = tmp; + } } if (ce->clone) { - ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->clone); + if (tmp != NULL) { + ce->clone = tmp; + } } if (ce->__get) { - ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__get); + if (tmp != NULL) { + ce->__get = tmp; + } } if (ce->__set) { - ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__set); + if (tmp != NULL) { + ce->__set = tmp; + } } if (ce->__call) { - ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__call); + if (tmp != NULL) { + ce->__call = tmp; + } } if (ce->serialize_func) { - ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + if (tmp != NULL) { + ce->serialize_func = tmp; + } } if (ce->unserialize_func) { - ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + if (tmp != NULL) { + ce->unserialize_func = tmp; + } } if (ce->__isset) { - ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__isset); + if (tmp != NULL) { + ce->__isset = tmp; + } } if (ce->__unset) { - ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__unset); + if (tmp != NULL) { + ce->__unset = tmp; + } } if (ce->__tostring) { - ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__tostring); + if (tmp != NULL) { + ce->__tostring = tmp; + } } if (ce->__callstatic) { - ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + if (tmp != NULL) { + ce->__callstatic = tmp; + } } if (ce->__debugInfo) { - ce->__debugInfo = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); + if (tmp != NULL) { + ce->__debugInfo = tmp; + } } // zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce); return 0;