}
}
+/* 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;
+ zend_string *filename = NULL;
+ int copy = 0;
+
+ 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)) {
+ if (function->op_array.filename != filename) {
+ filename = function->op_array.filename;
+ if (filename) {
+ copy = zend_hash_exists(preload_scripts, filename);
+ } else {
+ copy = 0;
+ }
+ }
+ if (copy) {
+ _zend_hash_append_ptr(dst, p->key, function);
+ } else {
+ orig_dtor(&p->val);
+ }
+ 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;
+ zend_string *filename = NULL;
+ int copy = 0;
+
+ 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)) {
+ if (ce->info.user.filename != filename) {
+ filename = ce->info.user.filename;
+ if (filename) {
+ copy = zend_hash_exists(preload_scripts, filename);
+ } else {
+ copy = 0;
+ }
+ }
+ if (copy) {
+ _zend_hash_append_ptr(dst, p->key, ce);
+ } else {
+ orig_dtor(&p->val);
+ }
+ 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 get_unresolved_initializer(zend_class_entry *ce, const char **kind, const char **name) {
+ zend_string *key;
+ zend_class_constant *c;
+ zend_property_info *prop;
+
+ *kind = "unknown";
+ *name = "";
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
+ *kind = "constant ";
+ *name = ZSTR_VAL(key);
+ }
+ } ZEND_HASH_FOREACH_END();
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop) {
+ zval *val;
+ if (prop->flags & ZEND_ACC_STATIC) {
+ val = &ce->default_static_members_table[prop->offset];
+ } else {
+ val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)];
+ }
+ if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
+ *kind = (prop->flags & ZEND_ACC_STATIC) ? "static property $" : "property $";
+ *name = ZSTR_VAL(key);
+ }
+ } ZEND_HASH_FOREACH_END();
+}
+
+static zend_bool preload_needed_types_known(zend_class_entry *ce);
+static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
+ zend_class_entry *p;
+ *kind = "Unknown reason";
+ *name = "";
+
+ if (ce->parent_name) {
+ zend_string *key = zend_string_tolower(ce->parent_name);
+ p = zend_hash_find_ptr(EG(class_table), key);
+ zend_string_release(key);
+ if (!p) {
+ *kind = "Unknown parent ";
+ *name = ZSTR_VAL(ce->parent_name);
+ return;
+ }
+ if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ *kind = "Parent with unresolved initializers ";
+ *name = ZSTR_VAL(ce->parent_name);
+ return;
+ }
+ if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
+ *kind = "Parent with unresolved property types ";
+ *name = ZSTR_VAL(ce->parent_name);
+ return;
+ }
+ }
+
+ if (ce->num_interfaces) {
+ uint32_t i;
+ for (i = 0; i < ce->num_interfaces; i++) {
+ p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
+ if (!p) {
+ *kind = "Unknown interface ";
+ *name = ZSTR_VAL(ce->interface_names[i].name);
+ return;
+ }
+ }
+ }
+
+ if (ce->num_traits) {
+ uint32_t i;
+ for (i = 0; i < ce->num_traits; i++) {
+ p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
+ if (!p) {
+ *kind = "Unknown trait ";
+ *name = ZSTR_VAL(ce->trait_names[i].name);
+ return;
+ }
+ }
+ }
+
+ if (!preload_needed_types_known(ce)) {
+ *kind = "Unknown type dependencies";
+ return;
+ }
+}
+
+static zend_bool preload_try_resolve_constants(zend_class_entry *ce)
+{
+ zend_bool ok, changed;
+ zend_class_constant *c;
+ zval *val;
+
+ EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
+ CG(in_compilation) = 1; /* prevent autoloading */
+ do {
+ ok = 1;
+ changed = 0;
+ 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) {
+ uint32_t i;
+ for (i = 0; i < ce->default_properties_count; i++) {
+ val = &ce->default_properties_table[i];
+ if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
+ zend_property_info *prop = ce->properties_info_table[i];
+ if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
+ ok = 0;
+ }
+ }
+ }
+ }
+ 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--;
+ }
+ }
+ } while (changed && !ok);
+ EG(exception) = NULL;
+ CG(in_compilation) = 0;
+
+ return ok;
+}
+
+static zend_bool preload_try_resolve_property_types(zend_class_entry *ce)
+{
+ zend_bool ok = 1;
+ zend_property_info *prop;
+ zend_class_entry *p;
+
+ if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
+ ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
+ zend_string *name, *lcname;
+
+ if (!ZEND_TYPE_IS_NAME(prop->type)) {
+ continue;
+ }
+
+ name = ZEND_TYPE_NAME(prop->type);
+ lcname = zend_string_tolower(name);
+ p = zend_hash_find_ptr(EG(class_table), lcname);
+ zend_string_release(lcname);
+ if (!p) {
+ ok = 0;
+ continue;
+ }
+ if (p != ce) {
+#ifdef ZEND_WIN32
+ /* On Windows we can't link with internal class, because of ASLR */
+ if (p->type == ZEND_INTERNAL_CLASS) {
+ ok = 0;
+ continue;
+ }
+#endif
+ if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ ok = 0;
+ continue;
+ }
+ if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
+ ok = 0;
+ continue;
+ }
+ }
+
+ zend_string_release(name);
+ prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type));
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ return ok;
+}
+
+static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type type) {
+ zend_string *name, *lcname;
+ zend_bool known;
+ if (!ZEND_TYPE_IS_NAME(type)) {
+ return 1;
+ }
+
+ name = ZEND_TYPE_NAME(type);
+ if (zend_string_equals_literal_ci(name, "self") ||
+ zend_string_equals_literal_ci(name, "parent") ||
+ zend_string_equals_ci(name, ce->name)) {
+ return 1;
+ }
+
+ lcname = zend_string_tolower(name);
+ known = zend_hash_exists(EG(class_table), lcname);
+ zend_string_release(lcname);
+ return known;
+}
+
+static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
+ zend_class_entry *p;
+ if (ce->trait_aliases || ce->trait_precedences) {
+ return 1;
+ }
+
+ if (ce->parent_name) {
+ zend_string *key = zend_string_tolower(ce->parent_name);
+ p = zend_hash_find_ptr(EG(class_table), key);
+ zend_string_release(key);
+ if (zend_hash_exists(&p->function_table, lcname)) {
+ return 1;
+ }
+ }
+
+ if (ce->num_interfaces) {
+ uint32_t i;
+ for (i = 0; i < ce->num_interfaces; i++) {
+ zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
+ if (zend_hash_exists(&p->function_table, lcname)) {
+ return 1;
+ }
+ }
+ }
+
+ if (ce->num_traits) {
+ uint32_t i;
+ for (i = 0; i < ce->num_traits; i++) {
+ zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
+ if (zend_hash_exists(&p->function_table, lcname)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static zend_bool preload_needed_types_known(zend_class_entry *ce) {
+ zend_function *fptr;
+ zend_string *lcname;
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) {
+ uint32_t i;
+ if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ if (!preload_is_type_known(ce, fptr->common.arg_info[-1].type) &&
+ preload_is_method_maybe_override(ce, lcname)) {
+ return 0;
+ }
+ }
+ for (i = 0; i < fptr->common.num_args; i++) {
+ if (!preload_is_type_known(ce, fptr->common.arg_info[i].type) &&
+ preload_is_method_maybe_override(ce, lcname)) {
+ return 0;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ return 1;
+}
+
+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;
+ dtor_func_t orig_dtor;
+ zend_function *function;
+
+ /* 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|ZEND_ACC_ANON_CLASS))
+ && !(ce->ce_flags & ZEND_ACC_LINKED)) {
+
+ if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
+ key = zend_string_tolower(ce->name);
+ if (zend_hash_exists(EG(class_table), key)) {
+ zend_string_release(key);
+ continue;
+ }
+ zend_string_release(key);
+ }
+
+ 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 (!(parent->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ continue;
+ }
+ if (!(parent->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
+ continue;
+ }
+ }
+
+ 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 (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ found = 0;
+ break;
+ }
+ }
+ 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;
+ }
+
+ /* TODO: This is much more restrictive than necessary. We only need to actually
+ * know the types for covariant checks, but don't need them if we can ensure
+ * compatibility through a simple string comparison. We could improve this using
+ * a more general version of zend_can_early_bind(). */
+ if (!preload_needed_types_known(ce)) {
+ continue;
+ }
+
+ zend_string *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)) {
+ /* Set filename & lineno information for inheritance errors */
+ CG(in_compilation) = 1;
+ CG(compiled_filename) = ce->info.user.filename;
+ if (ce->parent_name
+ && !ce->num_interfaces
+ && !ce->num_traits
+ && (parent->type == ZEND_INTERNAL_CLASS
+ || parent->info.user.filename == ce->info.user.filename)) {
+ /* simulate early binding */
+ CG(zend_lineno) = ce->info.user.line_end;
+ } else {
+ CG(zend_lineno) = ce->info.user.line_start;
+ }
+ zend_do_link_class(ce, NULL);
+ CG(in_compilation) = 0;
+ CG(compiled_filename) = NULL;
+
+ changed = 1;
+ }
+ }
+ if (ce->ce_flags & ZEND_ACC_LINKED) {
+ if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
+ || preload_try_resolve_constants(ce)) {
+ ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
+ changed = 1;
+ }
+ }
+
+ if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
+ if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
+ || preload_try_resolve_property_types(ce)) {
+ ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
+ changed = 1;
+ }
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ } while (changed);
+
+ /* 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_string *key = zend_string_tolower(ce->name);
+ if (zend_hash_exists(EG(class_table), key)) {
+ zend_error_at(
+ E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
+ "Can't preload already declared class %s", ZSTR_VAL(ce->name));
+ } else {
+ const char *kind, *name;
+ get_unlinked_dependency(ce, &kind, &name);
+ zend_error_at(
+ E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
+ "Can't preload unlinked class %s: %s%s",
+ ZSTR_VAL(ce->name), kind, name);
+ }
+ zend_string_release(key);
+ } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ const char *kind, *name;
+ get_unresolved_initializer(ce, &kind, &name);
+ zend_error_at(
+ E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
+ "Can't preload class %s with unresolved initializer for %s%s",
+ ZSTR_VAL(ce->name), kind, name);
+ } else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
+ zend_error_at(
+ E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
+ "Can't preload class %s with unresolved property types",
+ ZSTR_VAL(ce->name));
+ } else {
+ continue;
+ }
+ ce->ce_flags &= ~ZEND_ACC_PRELOADED;
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
+ if (EXPECTED(function->type == ZEND_USER_FUNCTION)
+ && function->common.scope == ce) {
+ function->common.fn_flags &= ~ZEND_ACC_PRELOADED;
+ }
+ } ZEND_HASH_FOREACH_END();
+ 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));
+
+ /* 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_CLASS_DELAYED:
+ key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
+ if (!zend_hash_exists(&script->script.class_table, key)) {
+ 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)
+{
+ if (is_stream_path(ZSTR_VAL(filename))) {
+ return NULL;
+ }
+ 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 || !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 && 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 void preload_register_trait_methods(zend_class_entry *ce) {
+ zend_op_array *op_array;
+ 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();
+}
+
+static void preload_fix_trait_methods(zend_class_entry *ce)
+{
+ zend_op_array *op_array;
+
+ 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();
+}
+
+static int preload_optimize(zend_persistent_script *script)
+{
+ zend_class_entry *ce;
+
+ zend_shared_alloc_init_xlat_table();
+
+ ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
+ if (ce->ce_flags & ZEND_ACC_TRAIT) {
+ preload_register_trait_methods(ce);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
+ ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
+ if (ce->ce_flags & ZEND_ACC_TRAIT) {
+ preload_register_trait_methods(ce);
+ }
+ } 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) {
+ preload_fix_trait_methods(ce);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
+ ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
+ if (ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS) {
+ preload_fix_trait_methods(ce);
+ }
+ } 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. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini.");
+ 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. Consider increasing the value for the opcache.memory_consumption directive in php.ini.");
+ 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;
+ char *orig_open_basedir;
+ size_t orig_map_ptr_last;
+ zval *zv;
+
+ ZCG(enabled) = 0;
+ ZCG(accelerator_enabled) = 0;
+ orig_open_basedir = PG(open_basedir);
+ PG(open_basedir) = NULL;
+ 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_PRELOAD;
+ 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;
+
+ orig_map_ptr_last = CG(map_ptr_last);
+
+ /* Compile and execute proloading script */
+ zend_stream_init_filename(&file_handle, (char *) config);
+
+ preload_scripts = emalloc(sizeof(HashTable));
+ zend_hash_init(preload_scripts, 0, NULL, NULL, 0);
+
+ zend_try {
+ zend_op_array *op_array;
+
+ ret = SUCCESS;
+ op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
+ if (file_handle.opened_path) {
+ zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
+ }
+ zend_destroy_file_handle(&file_handle);
+ if (op_array) {
+ zend_execute(op_array, NULL);
+ zend_exception_restore();
+ if (UNEXPECTED(EG(exception))) {
+ if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
+ zend_user_exception_handler();
+ }
+ if (EG(exception)) {
+ zend_exception_error(EG(exception), E_ERROR);
+ CG(unclean_shutdown) = 1;
+ ret = FAILURE;
+ }
+ }
+ destroy_op_array(op_array);
+ efree_size(op_array, sizeof(zend_op_array));
+ } else {
+ CG(unclean_shutdown) = 1;
+ ret = FAILURE;
+ }
+ } zend_catch {
+ ret = FAILURE;
+ } zend_end_try();
+
+ CG(compiler_options) = orig_compiler_options;
+ PG(open_basedir) = orig_open_basedir;
+ 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 ping_auto_globals_mask;
+ int i;
+ zend_class_entry *ce;
+ zend_op_array *op_array;
+
+ if (PG(auto_globals_jit)) {
+ ping_auto_globals_mask = zend_accel_get_auto_globals();
+ } else {
+ ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
+ }
+
+ /* Cleanup executor */
+ EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
+
+ php_call_shutdown_functions();
+ zend_call_destructors();
+ php_free_shutdown_functions();
+
+ /* 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);
+
+#if ZEND_DEBUG
+ if (gc_enabled() && !CG(unclean_shutdown)) {
+ gc_collect_cycles();
+ }
+#endif
+
+ zend_objects_store_free_object_storage(&EG(objects_store), 1);
+
+ /* Cleanup static variables of preloaded 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_PRELOADED);
+ 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_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ /* Cleanup static properties and variables of preloaded 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_PRELOADED);
+ 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_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
+ }
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ CG(map_ptr_last) = orig_map_ptr_last;
+
+ /* Inheritance errors may be thrown during linking */
+ zend_try {
+ preload_link();
+ } zend_catch {
+ CG(map_ptr_last) = orig_map_ptr_last;
+ ret = FAILURE;
+ goto finish;
+ } zend_end_try();
+
+ preload_remove_empty_includes();
+
+ /* Don't preload constants */
+ if (EG(zend_constants)) {
+ zend_string *key;
+ zval *zv;
+
+ /* Remember __COMPILER_HALT_OFFSET__(s) */
+ ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
+ zend_execute_data *orig_execute_data = EG(current_execute_data);
+ zend_execute_data fake_execute_data;
+ zval *offset;
+
+ memset(&fake_execute_data, 0, sizeof(fake_execute_data));
+ fake_execute_data.func = (zend_function*)&script->script.main_op_array;
+ EG(current_execute_data) = &fake_execute_data;
+ if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
+ script->compiler_halt_offset = Z_LVAL_P(offset);
+ }
+ EG(current_execute_data) = orig_execute_data;
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) {
+ zend_constant *c = Z_PTR_P(zv);
+ if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
+ break;
+ }
+ EG(zend_constants)->pDestructor(zv);
+ zend_string_release(key);
+ } ZEND_HASH_FOREACH_END_DEL();
+ }
+
+ script = create_persistent_script();
+ script->ping_auto_globals_mask = ping_auto_globals_mask;
+
+ /* 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();
+
+ /* Store method names first, because they may be shared between preloaded and non-preloaded classes */
+ ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
+ zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
+
+ if (!new_name) {
+ new_name = accel_new_interned_string(op_array->function_name);
+ zend_shared_alloc_register_xlat_entry(op_array->function_name, new_name);
+ }
+ op_array->function_name = new_name;
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+
+ 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) {
+ if (zend_hash_num_elements(&script->script.class_table) > 1) {
+ zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
+ }
+ 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();
+ } else {
+ CG(map_ptr_last) = orig_map_ptr_last;
+ }
+
+finish:
+ zend_hash_destroy(preload_scripts);
+ efree(preload_scripts);
+ preload_scripts = NULL;
+
+ return ret;
+}
+
+static size_t preload_ub_write(const char *str, size_t str_length)
+{
+ return fwrite(str, 1, str_length, stdout);
+}
+
+static void preload_flush(void *server_context)
+{
+ fflush(stdout);
+}
+
+static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s)
+{
+ return 0;
+}
+
+static int preload_send_headers(sapi_headers_struct *sapi_headers)
+{
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static void preload_send_header(sapi_header_struct *sapi_header, void *server_context)
+{
+}
+
+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 rc;
+ int orig_error_reporting;
+
+ 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 = preload_header_handler;
+ sapi_module.send_headers = preload_send_headers;
+ sapi_module.send_header = preload_send_header;
+ 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
+
+ orig_error_reporting = EG(error_reporting);
+ EG(error_reporting) = 0;
+
+ rc = php_request_startup();
+
+ EG(error_reporting) = orig_error_reporting;
+
+ if (rc == SUCCESS) {
+ zend_bool orig_report_memleaks;
+
+ /* 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;
+ }
+
+ orig_report_memleaks = PG(report_memleaks);
+ PG(report_memleaks) = 0;
++ /* We may not have registered signal handlers due to SIGG(reset)=0, so
++ * also disable the check that they are registered. */
++ SIGG(check) = 0;
+ php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
+ PG(report_memleaks) = orig_report_memleaks;
+ } else {
+ zend_shared_alloc_unlock();
+ 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;
+
+ sapi_activate();
+
+ return ret;
+ }
+
+ return SUCCESS;
+}
+
ZEND_EXT_API zend_extension zend_extension_entry = {
ACCELERATOR_PRODUCT_NAME, /* name */
PHP_VERSION, /* version */