]> granicus.if.org Git - php/commitdiff
Open Source Release
authorDmitry Stogov <dmitry@zend.com>
Wed, 13 Feb 2013 12:26:47 +0000 (16:26 +0400)
committerDmitry Stogov <dmitry@zend.com>
Wed, 13 Feb 2013 12:26:47 +0000 (16:26 +0400)
37 files changed:
.gitignore [new file with mode: 0644]
Optimizer/block_pass.c [new file with mode: 0644]
Optimizer/nop_removal.c [new file with mode: 0644]
Optimizer/optimize_temp_vars_5.c [new file with mode: 0644]
Optimizer/pass10.c [new file with mode: 0644]
Optimizer/pass1_5.c [new file with mode: 0644]
Optimizer/pass2.c [new file with mode: 0644]
Optimizer/pass3.c [new file with mode: 0644]
Optimizer/pass5.c [new file with mode: 0644]
Optimizer/pass9.c [new file with mode: 0644]
Optimizer/zend_optimizer.c [new file with mode: 0644]
Optimizer/zend_optimizer.h [new file with mode: 0644]
Optimizer/zend_optimizer_internal.h [new file with mode: 0644]
README [new file with mode: 0644]
ZendAccelerator.c [new file with mode: 0644]
ZendAccelerator.h [new file with mode: 0644]
config.m4 [new file with mode: 0644]
config.w32 [new file with mode: 0644]
shared_alloc_mmap.c [new file with mode: 0644]
shared_alloc_posix.c [new file with mode: 0644]
shared_alloc_shm.c [new file with mode: 0644]
shared_alloc_win32.c [new file with mode: 0644]
zend_accelerator_blacklist.c [new file with mode: 0644]
zend_accelerator_blacklist.h [new file with mode: 0644]
zend_accelerator_debug.c [new file with mode: 0644]
zend_accelerator_debug.h [new file with mode: 0644]
zend_accelerator_hash.c [new file with mode: 0644]
zend_accelerator_hash.h [new file with mode: 0644]
zend_accelerator_module.c [new file with mode: 0644]
zend_accelerator_module.h [new file with mode: 0644]
zend_accelerator_util_funcs.c [new file with mode: 0644]
zend_accelerator_util_funcs.h [new file with mode: 0644]
zend_persist.c [new file with mode: 0644]
zend_persist.h [new file with mode: 0644]
zend_persist_calc.c [new file with mode: 0644]
zend_shared_alloc.c [new file with mode: 0644]
zend_shared_alloc.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..8f2bec9
--- /dev/null
@@ -0,0 +1,87 @@
+# General Ignores
+*~
+.#*
+*.
+*.slo
+*.mk
+*.mem
+*.gcda
+*.gcno
+*.la
+*.lo
+*.o
+*.a
+*.ncb
+*.opt
+*.plg
+*swp
+*.patch
+*.tgz
+*.tar.gz
+*.tar.bz2
+.FBCIndex
+.FBCLockFolder
+.deps
+.libs
+phpt.*
+core
+dynlib.m4
+Debug
+Debug_TS
+Makefile
+Makefile.fragments
+Makefile.objects
+Makefile.global
+Release
+Release_TS
+Release_TSDbg
+Release_TS_inline
+Release_inline
+ZendEngine1
+_libs
+acconfig.h
+aclocal.m4
+acinclude.m4
+autom4te.cache
+bsd_converted
+buildconf.stamp
+buildmk.stamp
+confdefs.h
+config.h
+config.guess
+config.cache
+config.h.in
+config.log
+config.nice
+config.nice.bat
+config.status
+config.sub
+config_vars.mk
+configuration-parser.c
+configuration-parser.h
+configuration-parser.output
+configuration-scanner.c
+configure
+configure.in
+configure.bat
+configure.js
+conftest
+conftest.c
+debug.log
+diff
+generated_lists
+include
+install-sh
+internal_functions.c
+lcov_data
+lcov_html
+libs
+libtool
+ltmain.sh
+meta_cc
+meta_ccld
+missing
+mkinstalldirs
+modules
+build
+run-tests.php
diff --git a/Optimizer/block_pass.c b/Optimizer/block_pass.c
new file mode 100644 (file)
index 0000000..2481b25
--- /dev/null
@@ -0,0 +1,1967 @@
+#define DEBUG_BLOCKPASS 0
+
+/* Checks if a constant (like "true") may be relaced by its value */
+static int zend_get_persistent_constant(char *name, uint name_len, zval *result, int copy TSRMLS_DC ELS_DC)
+{
+       zend_constant *c;
+       char *lookup_name;
+       int retval = 1;
+       ALLOCA_FLAG(use_heap);
+
+       if (zend_hash_find(EG(zend_constants), name, name_len+1, (void **) &c) == FAILURE) {
+               lookup_name = DO_ALLOCA(name_len+1);
+               memcpy(lookup_name, name, name_len+1);
+               zend_str_tolower(lookup_name, name_len);
+
+               if (zend_hash_find(EG(zend_constants), lookup_name, name_len+1, (void **) &c)==SUCCESS) {
+                       if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) {
+                               retval=0;
+                       }
+               } else {
+                       retval=0;
+               }
+               FREE_ALLOCA(lookup_name);
+       }
+
+       if (retval) {
+               if(c->flags & CONST_PERSISTENT) {
+                       *result = c->value;
+                       if(copy) {
+                               zval_copy_ctor(result);
+                       }
+               } else {
+                       retval = 0;
+               }
+       }
+
+       return retval;
+}
+
+#if DEBUG_BLOCKPASS
+# define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1
+
+static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt)
+{
+       fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes+block->len-1, block->len);
+       if(!block->access) {
+               fprintf(stderr, " unused");
+       }
+       if(block->op1_to) {
+               fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes);
+       }
+       if(block->op2_to) {
+               fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes);
+       }
+       if(block->ext_to) {
+               fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes);
+       }
+       if(block->follow_to) {
+               fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes);
+       }
+
+       if(block->sources) {
+               zend_block_source *bs = block->sources;
+               fprintf(stderr, " s:");
+               while(bs) {
+                       fprintf(stderr, " %d", bs->from->start_opline - opcodes);
+                       bs = bs->next;
+               }
+       }
+
+       fprintf(stderr, "\n");
+       fflush(stderr);
+}
+#else
+#define print_block(a,b,c)
+#endif
+
+#define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1
+
+/* find code blocks in op_array
+   code block is a set of opcodes with single flow of control, i.e. without jmps,
+   branches, etc. */
+static zend_code_block *find_code_blocks(zend_op_array *op_array)
+{
+       zend_op *opline;
+       zend_op *end = op_array->opcodes+op_array->last;
+       zend_code_block *blocks = ecalloc(op_array->last+2, sizeof(zend_code_block));
+       zend_code_block *cur_block;
+       zend_uint opno = 0;
+
+       opline = op_array->opcodes;
+       blocks[0].start_opline = opline;
+       blocks[0].start_opline_no = 0;
+       /* first find block start points */
+       if(op_array->last_try_catch) {
+               int i;
+               blocks->try = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *));
+               blocks->catch = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *));
+               for(i=0; i< op_array->last_try_catch; i++) {
+                       blocks->try[i] = &blocks[op_array->try_catch_array[i].try_op];
+                       blocks->catch[i] = &blocks[op_array->try_catch_array[i].catch_op];
+                       START_BLOCK_OP(op_array->try_catch_array[i].try_op);
+                       START_BLOCK_OP(op_array->try_catch_array[i].catch_op);
+                       blocks[op_array->try_catch_array[i].try_op].is_try = 1;
+               }
+       }
+       while (opline<end) {
+               switch((unsigned)opline->opcode) {
+                       case ZEND_BRK:
+                       case ZEND_CONT:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                       case ZEND_GOTO:
+#endif
+                               /* would not optimize non-optimized BRK/CONTs - we cannot
+                                really know where it jumps, so these optimizations are
+                               too dangerous */
+                               efree(blocks);
+                               return NULL;
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                       case ZEND_FAST_CALL:
+                               START_BLOCK_OP(ZEND_OP1(opline).opline_num);
+                               break;
+#endif
+                       case ZEND_JMP:
+                               START_BLOCK_OP(ZEND_OP1(opline).opline_num);
+                               /* break missing intentionally */
+                       case ZEND_RETURN:
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       case ZEND_RETURN_BY_REF:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                       case ZEND_FAST_RET:
+#endif
+                       case ZEND_EXIT:
+                       case ZEND_THROW:
+                               /* start new block from this+1 */
+                               START_BLOCK_OP(opno+1);
+                               break;
+                               /* TODO: if conditional jmp depends on constant,
+                                                don't start block that won't be executed */
+                       case ZEND_CATCH:
+                               START_BLOCK_OP(opline->extended_value);
+                               START_BLOCK_OP(opno+1);
+                               break;
+                       case ZEND_JMPZNZ:
+                               START_BLOCK_OP(opline->extended_value);
+                       case ZEND_JMPZ:
+                       case ZEND_JMPNZ:
+                       case ZEND_JMPZ_EX:
+                       case ZEND_JMPNZ_EX:
+                       case ZEND_FE_FETCH:
+                       case ZEND_FE_RESET:
+                       case ZEND_NEW:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                       case ZEND_JMP_SET:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       case ZEND_JMP_SET_VAR:
+#endif
+                               START_BLOCK_OP(ZEND_OP2(opline).opline_num);
+                               START_BLOCK_OP(opno+1);
+                               break;
+
+               }
+               opno++;
+               opline++;
+       }
+
+       /* Build CFG (Control Flow Graph) */
+       cur_block = blocks;
+       for(opno = 1; opno < op_array->last; opno++) {
+               if(blocks[opno].start_opline) {
+                       /* found new block start */
+                       cur_block->len = blocks[opno].start_opline - cur_block->start_opline;
+                       cur_block->next = &blocks[opno];
+                       /* what is the last OP of previous block? */
+                       opline = blocks[opno].start_opline-1;
+                       switch((unsigned)opline->opcode) {
+                               case ZEND_RETURN:
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               case ZEND_RETURN_BY_REF:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               case ZEND_FAST_RET:
+#endif
+                               case ZEND_EXIT:
+                               case ZEND_THROW:
+                                       break;
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               case ZEND_FAST_CALL:
+#endif
+                               case ZEND_JMP:
+                                       cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
+                                       break;
+                               case ZEND_JMPZNZ:
+                                       cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
+                                       cur_block->ext_to = &blocks[opline->extended_value];
+                                       break;
+                               case ZEND_CATCH:
+                                       cur_block->ext_to = &blocks[opline->extended_value];
+                                       cur_block->follow_to = &blocks[opno];
+                                       break;
+                               case ZEND_JMPZ:
+                               case ZEND_JMPNZ:
+                               case ZEND_JMPZ_EX:
+                               case ZEND_JMPNZ_EX:
+                               case ZEND_FE_RESET:
+                               case ZEND_NEW:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                               case ZEND_JMP_SET:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               case ZEND_JMP_SET_VAR:
+#endif
+                               case ZEND_FE_FETCH:
+                                       cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
+                                       /* break missing intentionally */
+                         default:
+                                 /* next block follows this */
+                                       cur_block->follow_to = &blocks[opno];
+                                       break;
+                       }
+                       print_block(cur_block, op_array->opcodes, "");
+                       cur_block = cur_block->next;
+               }
+       }
+       cur_block->len = end - cur_block->start_opline;
+       cur_block->next = &blocks[op_array->last+1];
+       print_block(cur_block, op_array->opcodes, "");
+
+       /* The op_array desn't have BRK, CONT, GOTO opcodes anyway */
+       if(op_array->brk_cont_array) {
+               efree(op_array->brk_cont_array);
+       }
+       op_array->brk_cont_array = NULL;
+       op_array->last_brk_cont = 0;
+
+       return blocks;
+}
+
+/* CFG back references management */
+
+#define ADD_SOURCE(fromb, tob) { \
+       zend_block_source *__s = tob->sources; \
+    while(__s && __s->from != fromb) __s = __s->next; \
+       if(__s == NULL) { \
+               zend_block_source *__t = emalloc(sizeof(zend_block_source)); \
+               __t->next = tob->sources; \
+               tob->sources = __t; \
+               __t->from = fromb; \
+       } \
+}
+
+#define DEL_SOURCE(cs) { \
+       zend_block_source *__ns = (*cs)->next; \
+       efree(*cs); \
+       *cs = __ns; \
+}
+
+
+static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new)
+{
+       /* replace all references to 'old' in 'list' with 'new' */
+       zend_block_source **cs;
+       int found = 0;
+
+       for(cs = &list; *cs; cs = &((*cs)->next)) {
+               if((*cs)->from == new) {
+                       if(found) {
+                               DEL_SOURCE(cs);
+                       } else {
+                               found = 1;
+                       }
+               }
+
+               if((*cs)->from == old) {
+                       if(found) {
+                               DEL_SOURCE(cs);
+                       } else {
+                               (*cs)->from = new;
+                               found = 1;
+                       }
+               }
+       }
+}
+
+static inline void del_source(zend_code_block *from, zend_code_block *to)
+{
+       /* delete source 'from' from 'to'-s sources list */
+       zend_block_source **cs = &to->sources;
+
+       if(to->sources == NULL) {
+               to->access = 0;
+               return;
+       }
+
+       while(*cs) {
+               if((*cs)->from == from) {
+                       DEL_SOURCE(cs);
+                       break;
+               }
+               cs = &((*cs)->next);
+       }
+
+       if(to->sources == NULL) {
+               /* 'to' has no more sources - it's unused, will be stripped */
+               to->access = 0;
+               return;
+       }
+
+       if(to->sources->next == NULL) {
+               /* source to only one block */
+               zend_code_block *from_block = to->sources->from;
+               
+               if(from_block->access && from_block->follow_to == to &&
+                  from_block->op1_to == NULL &&
+                  from_block->op2_to == NULL &&
+                  from_block->ext_to == NULL
+                  ) {
+                       /* this block follows it's only predecessor - we can join them */
+                       zend_op *new_to = from_block->start_opline + from_block->len;
+                       if(new_to != to->start_opline) {
+                               /* move block to new location */
+                               memmove(new_to, to->start_opline, sizeof(zend_op)*to->len);
+                       }
+                       /* join blocks' lengthes */
+                       from_block->len += to->len;
+                       /* move 'to'`s references to 'from' */
+                       to->start_opline = NULL;
+                       to->access = 0;
+                       efree(to->sources);
+                       to->sources = NULL;
+                       from_block->follow_to = to->follow_to;
+                       if(to->op1_to) {
+                               from_block->op1_to = to->op1_to;
+                               replace_source(to->op1_to->sources, to, from_block);
+                       }
+                       if(to->op2_to) {
+                               from_block->op2_to = to->op2_to;
+                               replace_source(to->op2_to->sources, to, from_block);
+                       }
+                       if(to->ext_to) {
+                               from_block->ext_to = to->ext_to;
+                               replace_source(to->ext_to->sources, to, from_block);
+                       }
+                       if(to->follow_to) {
+                               replace_source(to->follow_to->sources, to, from_block);
+                       }
+                       /* remove "to" from list */
+               }
+       }
+}
+
+static void delete_code_block(zend_code_block *block)
+{
+       if(block->follow_to) {
+               zend_block_source *bs = block->sources;
+               while(bs) {
+                       zend_code_block *from_block = bs->from;
+                       zend_code_block *to = block->follow_to;
+                       if(from_block->op1_to == block) {
+                               from_block->op1_to = to;
+                               ADD_SOURCE(from_block, to);
+                       }
+                       if(from_block->op2_to == block) {
+                               from_block->op2_to = to;
+                               ADD_SOURCE(from_block, to);
+                       }
+                       if(from_block->ext_to == block) {
+                               from_block->ext_to = to;
+                               ADD_SOURCE(from_block, to);
+                       }
+                       if(from_block->follow_to == block) {
+                               from_block->follow_to = to;
+                               ADD_SOURCE(from_block, to);
+                       }
+                       bs = bs->next;
+               }
+       }
+       block->access = 0;
+}
+
+static void zend_access_path(zend_code_block *block)
+{
+       if(block->access) {
+               return;
+       }
+
+       block->access = 1;
+       if(block->op1_to) {
+               zend_access_path(block->op1_to);
+               ADD_SOURCE(block, block->op1_to);
+       }
+       if(block->op2_to) {
+               zend_access_path(block->op2_to);
+               ADD_SOURCE(block, block->op2_to);
+       }
+       if(block->ext_to) {
+               zend_access_path(block->ext_to);
+               ADD_SOURCE(block, block->ext_to);
+       }
+       if(block->follow_to) {
+               zend_access_path(block->follow_to);
+               ADD_SOURCE(block, block->follow_to);
+       }
+}
+
+/* Traverse CFG, mark reachable basic blocks and build back references */
+static void zend_rebuild_access_path(zend_code_block *blocks, zend_op_array *op_array, int find_start)
+{
+       zend_code_block *start = find_start?NULL:blocks;
+       zend_code_block *b;
+
+       /* Mark all blocks as unaccessable and destroy back references */
+       b = blocks;
+       while (b != NULL) {
+               zend_block_source *cs;
+               if (!start && b->access) {
+                       start = b;
+               }
+               b->access = 0;
+               cs = b->sources;
+               while(cs) {
+                       zend_block_source *n = cs->next;
+                       efree(cs);
+                       cs = n;
+               }
+               b->sources = NULL;
+               b = b->next;
+       }
+
+       /* Walk thorough all pathes */
+       zend_access_path(start);
+
+       /* Add exception pathes */
+       if (op_array->last_try_catch) {
+               int i;
+               for(i=0; i< op_array->last_try_catch; i++) {
+                       if(!blocks->catch[i]->access) {
+                               zend_access_path(blocks->catch[i]);
+                       }
+               }
+       }
+}
+
+/* Data dependencies macros */
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+
+# define VAR_NUM_EX(op) ((op ## _type & (IS_TMP_VAR|IS_VAR))?VAR_NUM((op).var):(op).var)
+
+# define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
+# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
+
+# define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0)
+
+#else
+
+# define VAR_NUM_EX(op) ((op).op_type==IS_TMP_VAR||(op).op_type==IS_VAR?VAR_NUM((op).u.var):(op).u.var)
+
+# define VAR_SOURCE(op) Tsource[VAR_NUM(op.u.var)]
+# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(ZEND_RESULT(opline).var)] = opline
+
+# define VAR_UNSET(op) do { if ((op).op_type==IS_TMP_VAR||(op).op_type==IS_VAR) {VAR_SOURCE(op) = NULL;}} while (0)
+
+#endif
+
+#define convert_to_string_safe(v) \
+       if(Z_TYPE_P((v)) == IS_NULL) { \
+               ZVAL_STRINGL((v), "", 0, 1); \
+       } else { \
+               convert_to_string((v)); \
+       }
+
+static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, char *used_ext TSRMLS_DC)
+{
+       zend_op *opline = block->start_opline;
+       zend_op *end, *last_op = NULL;
+       zend_op **Tsource = NULL;
+
+       print_block(block, op_array->opcodes, "Opt ");
+
+       /* remove leading NOPs */
+       while(block->start_opline->opcode == ZEND_NOP) {
+               if(block->len == 1) {
+                       /* this block is all NOPs, join with following block */
+                       if(block->follow_to) {
+                               delete_code_block(block);
+                       }
+                       return;
+               }
+               block->start_opline++;
+               block->start_opline_no++;
+               block->len--;
+       }
+
+       /* we track data dependencies only insight a single basic block */
+       if(op_array->T){
+               Tsource = ecalloc(op_array->T, sizeof(zend_op *));
+       }
+       opline = block->start_opline;
+       end = opline+block->len;
+       while((op_array->T)&&(opline<end)) {
+               /* strip X = QM_ASSIGN(const) */
+               if(ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) &&
+                  VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN &&
+                  ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
+                  opline->opcode != ZEND_CASE &&         /* CASE _always_ expects variable */
+                  opline->opcode != ZEND_FETCH_DIM_TMP_VAR &&   /* in 5.1, FETCH_DIM_TMP_VAR expects T */
+                  opline->opcode != ZEND_FE_RESET &&
+                  opline->opcode != ZEND_FREE
+                  ) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+                       MAKE_NOP(src);
+               }
+
+               /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */
+               if(ZEND_OP2_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op2) &&
+                  VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN &&
+                  ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) {
+                       zend_op *src = VAR_SOURCE(opline->op2);
+                       VAR_UNSET(opline->op2);
+                       COPY_NODE(opline->op2, src->op1);
+                       MAKE_NOP(src);
+               }
+
+               /* T = PRINT(X), F(T) => ECHO(X), F(1) */
+               if(ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) &&
+                  VAR_SOURCE(opline->op1)->opcode == ZEND_PRINT &&
+                  opline->opcode != ZEND_CASE && opline->opcode != ZEND_FREE) {
+                       ZEND_OP1_TYPE(opline) = IS_CONST;
+                       LITERAL_LONG(opline->op1, 1);
+               }
+
+               if(ZEND_OP2_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op2) &&
+                  VAR_SOURCE(opline->op2)->opcode == ZEND_PRINT) {
+                       ZEND_OP2_TYPE(opline) = IS_CONST;
+                       LITERAL_LONG(opline->op2, 1);
+               }
+
+               /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
+               if((opline->opcode == ZEND_ECHO || opline->opcode == ZEND_PRINT) &&
+                  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) &&
+                  VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
+                  VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+                       MAKE_NOP(src);
+               }
+
+               /* T = PRINT(X), FREE(T) => ECHO(X) */
+               if(opline->opcode == ZEND_FREE &&
+                       ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1)) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       if(src->opcode == ZEND_PRINT) {
+                               src->opcode = ZEND_ECHO;
+                               ZEND_RESULT_TYPE(src) = IS_UNUSED;
+                               MAKE_NOP(opline);
+                       }
+               }
+
+       /* T = BOOL(X), FREE(T) => NOP */
+               if(opline->opcode == ZEND_FREE &&
+           ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1)) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       if (src->opcode == ZEND_BOOL) {
+                               if (ZEND_OP1_TYPE(src) == IS_CONST) {
+                                       literal_dtor(&ZEND_OP1_LITERAL(src));
+                               }
+                               MAKE_NOP(src);
+                               MAKE_NOP(opline);
+                       }
+               }
+
+#if 0
+               /* pre-evaluate functions:
+                  constant(x)
+                  defined(x)
+                  function_exists(x)
+                  extension_loaded(x)
+                  BAD: interacts badly with Accelerator
+               */
+               if((ZEND_OP1_TYPE(opline) & IS_VAR) &&
+                  VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
+                  VAR_SOURCE(opline->op1)->extended_value == 1) {
+                       zend_op *fcall = VAR_SOURCE(opline->op1);
+                       zend_op *sv = fcall-1;
+                       if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
+                          ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
+                          Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
+                          ) {
+                               zval *arg = &OPLINE_OP1_LITERAL(sv);
+                               char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
+                               int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
+                               if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) {
+                                       zval c;
+                                       if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 0 TSRMLS_CC ELS_CC) != 0) {
+                                               literal_dtor(arg);
+                                               MAKE_NOP(sv);
+                                               MAKE_NOP(fcall);
+                                               LITERAL_BOOL(opline->op1, 1);
+                                               ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       }
+                               } else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
+                                                 (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
+                                                 ) {
+                                       zend_function *function;
+                                       if(zend_hash_find(EG(function_table), Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1, (void **)&function) == SUCCESS) {
+                                               literal_dtor(arg);
+                                               MAKE_NOP(sv);
+                                               MAKE_NOP(fcall);
+                                               LITERAL_BOOL(opline->op1, 1);
+                                               ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       }
+                               } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
+                                       zval c;
+                                       if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 1 TSRMLS_CC ELS_CC) != 0) {
+                                               literal_dtor(arg);
+                                               MAKE_NOP(sv);
+                                               MAKE_NOP(fcall);
+                                               ZEND_OP1_LITERAL(opline) = zend_add_literal(op_array, &c TSRMLS_CC);
+                                               /* no copy ctor - get already copied it */
+                                               ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       }
+                               } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
+                                       if(zend_hash_exists(&module_registry, Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1)) {
+                                               literal_dtor(arg);
+                                               MAKE_NOP(sv);
+                                               MAKE_NOP(fcall);
+                                               LITERAL_BOOL(opline->op1, 1);
+                                               ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       }
+                               }
+                       }
+               }
+#endif
+
+        /* IS_EQ(TRUE, X)      => BOOL(X)
+         * IS_EQ(FALSE, X)     => BOOL_NOT(X)
+         * IS_NOT_EQ(TRUE, X)  => BOOL_NOT(X)
+         * IS_NOT_EQ(FALSE, X) => BOOL(X)
+         */
+               if(opline->opcode == ZEND_IS_EQUAL ||
+                  opline->opcode == ZEND_IS_NOT_EQUAL) {
+                       if (ZEND_OP1_TYPE(opline) == IS_CONST &&
+                               Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_BOOL) {
+                               opline->opcode =
+                                       ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP1_LITERAL(opline)))?
+                                       ZEND_BOOL:ZEND_BOOL_NOT;
+                               COPY_NODE(opline->op1, opline->op2);
+                               SET_UNUSED(opline->op2);
+                       } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
+                                          Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_BOOL) {
+                               opline->opcode =
+                                       ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP2_LITERAL(opline)))?
+                                       ZEND_BOOL:ZEND_BOOL_NOT;
+                               SET_UNUSED(opline->op2);
+                       }
+               }
+
+               if((opline->opcode == ZEND_BOOL ||
+                       opline->opcode == ZEND_BOOL_NOT ||
+                       opline->opcode == ZEND_JMPZ ||
+                       opline->opcode == ZEND_JMPNZ ||
+                       opline->opcode == ZEND_JMPZNZ) &&
+                  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) != NULL &&
+                  !used_ext[VAR_NUM(ZEND_OP1(opline).var)] &&
+                  VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT
+                  ) {
+                       /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+
+                       COPY_NODE(opline->op1, src->op1);
+
+                       switch(opline->opcode) {
+                               case ZEND_BOOL:
+                                       /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
+                                       opline->opcode = ZEND_BOOL_NOT;
+                                       break;
+                               case ZEND_BOOL_NOT:
+                                       /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */
+                                       opline->opcode = ZEND_BOOL;
+                                       break;
+                               case ZEND_JMPZ:
+                                       /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */
+                                       opline->opcode = ZEND_JMPNZ;
+                                       break;
+                               case ZEND_JMPNZ:
+                                       /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */
+                                       opline->opcode = ZEND_JMPZ;
+                                       break;
+                               case ZEND_JMPZNZ:
+                               {
+                                       /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
+                                       int op_t;
+                                       zend_code_block *op_b;
+
+                                       op_t = opline->extended_value;
+                                       opline->extended_value = ZEND_OP2(opline).opline_num;
+                                       ZEND_OP2(opline).opline_num = op_t;
+
+                                       op_b = block->ext_to;
+                                       block->ext_to = block->op2_to;
+                                       block->op2_to = op_b;
+                               }
+                               break;
+                       }
+
+                       VAR_UNSET(opline->op1);
+                       MAKE_NOP(src);
+                       continue;
+               } else
+#if 0
+               /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */
+               if(0 && (opline->opcode == ZEND_JMPZ_EX ||
+                       opline->opcode == ZEND_JMPNZ_EX) &&
+                  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) != NULL &&
+                  VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT &&
+                  ZEND_OP1(opline).var == ZEND_RESULT(opline).var
+                  ) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       if(opline->opcode == ZEND_JMPZ_EX) {
+                               opline->opcode = ZEND_JMPNZ;
+                       } else {
+                               opline->opcode = ZEND_JMPZ;
+                       }
+                       COPY_NODE(opline->op1, src->op1);
+                       SET_UNUSED(opline->result);
+                       continue;
+               } else
+#endif
+               /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */
+               if((opline->opcode == ZEND_BOOL ||
+                       opline->opcode == ZEND_BOOL_NOT ||
+                       opline->opcode == ZEND_JMPZ ||
+                       opline->opcode == ZEND_JMPZ_EX ||
+                       opline->opcode == ZEND_JMPNZ_EX ||
+                       opline->opcode == ZEND_JMPNZ ||
+                       opline->opcode == ZEND_JMPZNZ) &&
+                  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                  VAR_SOURCE(opline->op1) != NULL &&
+                  (!used_ext[VAR_NUM(ZEND_OP1(opline).var)] ||
+                       (ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
+                        ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) &&
+                  (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
+                       VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)
+                  ) {
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+
+                       VAR_UNSET(opline->op1);
+                       MAKE_NOP(src);
+                       continue;
+               } else if(last_op && opline->opcode == ZEND_ECHO &&
+                                 last_op->opcode == ZEND_ECHO &&
+                                 ZEND_OP1_TYPE(opline) == IS_CONST &&
+                                 Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
+                                 ZEND_OP1_TYPE(last_op) == IS_CONST &&
+                                 Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
+                       /* compress consecutive ECHO's.
+                        * Float to string conversion may be affected by current
+                        * locale setting.
+                        */
+                       int l;
+
+                       if(Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+                               convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
+                       }
+                       if(Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
+                               convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
+                       }
+                       l = Z_STRLEN(ZEND_OP1_LITERAL(opline)) + Z_STRLEN(ZEND_OP1_LITERAL(last_op));
+                       if (IS_INTERNED(Z_STRVAL(ZEND_OP1_LITERAL(last_op)))) {
+                               char *tmp = emalloc(l + 1);
+                               memcpy(tmp, Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1);
+                               Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = tmp;
+                       } else {
+                               Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = erealloc(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l+1);
+                       }
+                       memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op))+Z_STRLEN(ZEND_OP1_LITERAL(last_op)), Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
+                       Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
+                       zval_dtor(&ZEND_OP1_LITERAL(opline));
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       Z_STRVAL(ZEND_OP1_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1, 1 TSRMLS_CC);
+                       Z_TYPE(ZEND_OP1_LITERAL(last_op)) = IS_NULL;
+#else
+                       Z_STRVAL(ZEND_OP1_LITERAL(opline)) = Z_STRVAL(ZEND_OP1_LITERAL(last_op));
+#endif
+                       Z_STRLEN(ZEND_OP1_LITERAL(opline)) = l;
+                       MAKE_NOP(last_op);
+               } else if(opline->opcode == ZEND_CONCAT &&
+                                 ZEND_OP2_TYPE(opline) == IS_CONST &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_STRING) &&
+                                 ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
+                                 ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var
+                                 ) {
+                       /* compress consecutive CONCATs */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       int l;
+
+                       if(Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
+                               convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
+                       }
+                       if(Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
+                               convert_to_string_safe(&ZEND_OP2_LITERAL(src));
+                       }
+
+                       VAR_UNSET(opline->op1);
+                       if (ZEND_OP1_TYPE(src) == IS_UNUSED) {
+                               /* 5.3 may use IS_UNUSED as first argument to ZEND_ADD_... */
+                               opline->opcode = ZEND_ADD_STRING;
+                       }
+                       COPY_NODE(opline->op1, src->op1);
+                       l = Z_STRLEN(ZEND_OP2_LITERAL(opline)) + Z_STRLEN(ZEND_OP2_LITERAL(src));
+                       if (IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(src)))) {
+                               char *tmp = emalloc(l + 1);
+                               memcpy(tmp, Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1);
+                               Z_STRVAL(ZEND_OP2_LITERAL(src)) = tmp;
+                       } else {
+                               Z_STRVAL(ZEND_OP2_LITERAL(src)) = erealloc(Z_STRVAL(ZEND_OP2_LITERAL(src)), l+1);
+                       }
+                       memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src))+Z_STRLEN(ZEND_OP2_LITERAL(src)), Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
+                       Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
+                       if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) {
+                               efree(Z_STRVAL(ZEND_OP2_LITERAL(opline)));
+                       }
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       Z_STRVAL(ZEND_OP2_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1, 1 TSRMLS_CC);
+                       Z_TYPE(ZEND_OP2_LITERAL(src)) = IS_NULL;
+#else
+                       Z_STRVAL(ZEND_OP2_LITERAL(opline)) = Z_STRVAL(ZEND_OP2_LITERAL(src));
+#endif
+                       Z_STRLEN(ZEND_OP2_LITERAL(opline)) = l;
+                       MAKE_NOP(src);
+               } else if((opline->opcode == ZEND_ADD_STRING ||
+                                 opline->opcode == ZEND_ADD_VAR) &&
+                                 ZEND_OP1_TYPE(opline) == IS_CONST) {
+                       /* convert ADD_STRING(C1, C2) to CONCAT(C1, C2) */
+                       opline->opcode = ZEND_CONCAT;
+                       continue;
+               } else if(opline->opcode == ZEND_ADD_CHAR && ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP2_TYPE(opline) == IS_CONST) {
+            /* convert ADD_CHAR(C1, C2) to CONCAT(C1, C2) */
+                       char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
+                       ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
+                       opline->opcode = ZEND_CONCAT;
+                       continue;
+               } else if(
+                       (opline->opcode == ZEND_ADD ||
+                        opline->opcode == ZEND_SUB ||
+                        opline->opcode == ZEND_MUL ||
+                        opline->opcode == ZEND_DIV ||
+                        opline->opcode == ZEND_MOD ||
+                        opline->opcode == ZEND_SL ||
+                        opline->opcode == ZEND_SR ||
+                        opline->opcode == ZEND_CONCAT ||
+                        opline->opcode == ZEND_IS_EQUAL ||
+                        opline->opcode == ZEND_IS_NOT_EQUAL ||
+                        opline->opcode == ZEND_IS_SMALLER ||
+                        opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
+                        opline->opcode == ZEND_IS_IDENTICAL ||
+                        opline->opcode == ZEND_IS_NOT_IDENTICAL ||
+                        opline->opcode == ZEND_BOOL_XOR ||
+                        opline->opcode == ZEND_BW_OR ||
+                        opline->opcode == ZEND_BW_AND ||
+                        opline->opcode == ZEND_BW_XOR) &&
+                       ZEND_OP1_TYPE(opline)==IS_CONST &&
+                       ZEND_OP2_TYPE(opline)==IS_CONST) {
+                       /* evaluate constant expressions */
+                       int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode);
+                       zval result;
+                       int er;
+
+            if ((opline->opcode == ZEND_DIV ||
+                 opline->opcode == ZEND_MOD) &&
+                ((Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG &&
+                  Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) ||
+                 (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_DOUBLE &&
+                  Z_DVAL(ZEND_OP2_LITERAL(opline)) == 0.0))) {
+                               if(RESULT_USED(opline)) {
+                                       SET_VAR_SOURCE(opline);
+                               }
+                opline++;
+                               continue;
+                       }
+                       er = EG(error_reporting);
+                       EG(error_reporting) = 0;
+                       if(binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) == SUCCESS) {
+                               PZ_SET_REFCOUNT_P(&result, 1);
+                               PZ_UNSET_ISREF_P(&result);
+
+                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                               literal_dtor(&ZEND_OP2_LITERAL(opline));
+                               ZEND_OP1_LITERAL(opline) = result;
+                               SET_UNUSED(opline->op2);
+
+                               opline->opcode = ZEND_QM_ASSIGN;
+                       }
+                       EG(error_reporting) = er;
+               } else if((opline->opcode == ZEND_BOOL ||
+                                  opline->opcode == ZEND_BOOL_NOT ||
+                                  opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline)==IS_CONST
+                                  ) {
+                       /* evaluate constant unary ops */
+                       unary_op_type unary_op = get_unary_op(opline->opcode);
+                       zval result;
+
+                       if(unary_op) {
+                               unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC);
+                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                       } else {
+                               /* BOOL */
+                               result = ZEND_OP1_LITERAL(opline);
+                               convert_to_boolean(&result);
+                       }
+                       PZ_SET_REFCOUNT_P(&result, 1);
+                       PZ_UNSET_ISREF_P(&result);
+                       ZEND_OP1_LITERAL(opline) = result;
+                       opline->opcode = ZEND_QM_ASSIGN;
+               } else if((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) &&
+                                  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                  VAR_SOURCE(opline->op1) &&
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN
+                                  ) {
+                       /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+                       MAKE_NOP(src);
+               } else if((opline->opcode == ZEND_ADD_STRING ||
+                                  opline->opcode == ZEND_ADD_CHAR) &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) {
+                       /* convert T = INIT_STRING(), T = ADD_STRING(T, X) to T = QM_ASSIGN(X) */
+                       /* CHECKME: Remove ZEND_ADD_VAR optimizsation, since some conversions -
+                          namely, BOOL(false)->string - don't allocate memory but use empty_string
+                          and ADD_CHAR fails */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, opline->op2);
+                       if(opline->opcode == ZEND_ADD_CHAR) {
+                               char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
+                               ZVAL_STRINGL(&ZEND_OP1_LITERAL(opline), &c, 1, 1);
+                       }
+                       SET_UNUSED(opline->op2);
+                       MAKE_NOP(src);
+                       opline->opcode = ZEND_QM_ASSIGN;
+               } else if((opline->opcode == ZEND_ADD_STRING ||
+                                  opline->opcode == ZEND_ADD_CHAR ||
+                                  opline->opcode == ZEND_ADD_VAR ||
+                                  opline->opcode == ZEND_CONCAT) &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT &&
+                                 ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
+                                 Z_TYPE(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == IS_STRING &&
+                                 Z_STRLEN(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == 0) {
+                       /* convert T = CONCAT(X,''), T = ADD_STRING(T, Y) to T = CONCAT(X,Y) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+                       if(opline->opcode == ZEND_ADD_CHAR) {
+                               char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
+                               ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
+                       }
+                       opline->opcode = ZEND_CONCAT;
+                       literal_dtor(&ZEND_OP2_LITERAL(src)); /* will take care of empty_string too */
+                       MAKE_NOP(src);
+               } else if(opline->opcode == ZEND_ADD_VAR &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) {
+                       /* convert T = INIT_STRING(), T = ADD_VAR(T, X) to T = CAST(STRING, X) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, opline->op2);
+                       SET_UNUSED(opline->op2);
+                       MAKE_NOP(src);
+                       opline->opcode = ZEND_CAST;
+                       opline->extended_value = IS_STRING;
+               } else if((opline->opcode == ZEND_ADD_STRING ||
+                                  opline->opcode == ZEND_ADD_CHAR ||
+                                  opline->opcode == ZEND_ADD_VAR ||
+                                  opline->opcode == ZEND_CONCAT) &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
+                                 VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
+                       /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       VAR_UNSET(opline->op1);
+                       COPY_NODE(opline->op1, src->op1);
+                       if(opline->opcode == ZEND_ADD_CHAR) {
+                               char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
+                               ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
+                       }
+                       opline->opcode = ZEND_CONCAT;
+                       MAKE_NOP(src);
+               } else if(opline->opcode == ZEND_QM_ASSIGN &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
+                                 ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
+                       /* strip T = QM_ASSIGN(T) */
+                       MAKE_NOP(opline);
+               } else if(opline->opcode == ZEND_BOOL &&
+                                 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                 VAR_SOURCE(opline->op1) &&
+                                 (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR ||
+                                  VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ
+                                  ) && !used_ext[VAR_NUM(ZEND_OP1(opline).var)]
+                                 ) {
+                       /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */
+                       zend_op *src = VAR_SOURCE(opline->op1);
+                       COPY_NODE(src->result, opline->result);
+                       SET_VAR_SOURCE(src);
+                       MAKE_NOP(opline);
+               }
+               /* get variable source */
+               if(RESULT_USED(opline)) {
+                       SET_VAR_SOURCE(opline);
+               }
+               if(opline->opcode != ZEND_NOP) {
+                       last_op = opline;
+               }
+               opline++;
+       }
+
+       /* remove leading NOPs */
+       while(block->start_opline->opcode == ZEND_NOP) {
+               if(block->len == 1) {
+                       /* this block is all NOPs, join with following block */
+                       if(block->follow_to) {
+                               delete_code_block(block);
+                       }
+                       if(op_array->T){
+                               efree(Tsource);
+                       }
+                       return;
+               }
+               block->start_opline++;
+               block->start_opline_no++;
+               block->len--;
+       }
+
+       /* strip the inside NOPs */
+       opline = block->start_opline;
+       end = opline+block->len;
+       while(opline<end) {
+               if(opline->opcode == ZEND_NOP) {
+                       zend_op *nop = opline+1;
+                       int noplen;
+                       while(nop<end && nop->opcode == ZEND_NOP) {
+                               nop++;
+                       }
+                       noplen = nop-opline;
+                       if(nop<end) {
+                               /* move up non-NOP opcodes */
+                               memmove(opline, nop, (end-nop)*sizeof(zend_op));
+                       } else {
+                               /* all NOPs up to the end, do nothing */
+                       }
+                       block->len -= noplen;
+                       end = block->start_opline + block->len;
+               }
+               opline++;
+       }
+
+       if(op_array->T){
+               efree(Tsource);
+       }
+}
+
+/* Rebuild plain (optimized) op_array from CFG */
+static void assemble_code_blocks(zend_code_block *blocks, zend_op_array *op_array)
+{
+       zend_op *new_opcodes = emalloc(op_array->last*sizeof(zend_op));
+       zend_op *opline = new_opcodes;
+       zend_code_block *cur_block = blocks;
+
+       /* Copy code of reachable blocks into a single buffer */
+       while(cur_block) {
+               if(cur_block->access) {
+                       memcpy(opline, cur_block->start_opline, cur_block->len*sizeof(zend_op));
+                       cur_block->start_opline = opline;
+                       opline += cur_block->len;
+                       if((opline-1)->opcode == ZEND_JMP) {
+                               zend_code_block *next;
+                               next = cur_block->next;
+                               while(next && !next->access) {
+                                       next = next->next;
+                               }
+                               if(next && next == cur_block->op1_to) {
+                                       /* JMP to the next block - strip it */
+                                       cur_block->follow_to = cur_block->op1_to;
+                                       cur_block->op1_to = NULL;
+                                       MAKE_NOP((opline-1));
+                                       opline--;
+                                       cur_block->len--;
+                               }
+                       }
+               } else {
+                       /* this block will not be used, delete all constants there */
+                       zend_op *_opl;
+                       zend_op *end = cur_block->start_opline+cur_block->len;
+                       for(_opl = cur_block->start_opline; _opl && _opl < end; _opl++) {
+                               if(ZEND_OP1_TYPE(_opl) == IS_CONST) {
+                                       literal_dtor(&ZEND_OP1_LITERAL(_opl));
+                               }
+                               if(ZEND_OP2_TYPE(_opl) == IS_CONST) {
+                                       literal_dtor(&ZEND_OP2_LITERAL(_opl));
+                               }
+                       }
+               }
+               cur_block = cur_block->next;
+       }
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       if(opline[-1].opcode == ZEND_THROW) {
+               /* if we finished with THROW, we need to add space between THROW and HANDLE to not confuse
+                  zend_throw_internal */
+               MAKE_NOP(opline);
+               opline->lineno = opline[-1].lineno;
+               opline++;
+       }
+       MAKE_NOP(opline);
+       opline->opcode = ZEND_HANDLE_EXCEPTION;
+       opline->lineno = opline[-1].lineno;
+       opline++;
+#endif
+
+       op_array->last = opline-new_opcodes;
+
+       /* adjust exception jump targets */
+       if(op_array->last_try_catch) {
+               int i;
+               for(i=0; i< op_array->last_try_catch; i++) {
+                       op_array->try_catch_array[i].try_op = blocks->try[i]->start_opline - new_opcodes;
+                       op_array->try_catch_array[i].catch_op = blocks->catch[i]->start_opline - new_opcodes;
+               }
+               efree(blocks->try);
+               efree(blocks->catch);
+       }
+
+    /* adjust jump targets */
+       for(cur_block = blocks; cur_block; cur_block = cur_block->next) {
+               if(!cur_block->access) {
+                       continue;
+               }
+               if(cur_block->op1_to) {
+                       ZEND_OP1(&cur_block->start_opline[cur_block->len-1]).opline_num = cur_block->op1_to->start_opline - new_opcodes;
+               }
+               if(cur_block->op2_to) {
+                       ZEND_OP2(&cur_block->start_opline[cur_block->len-1]).opline_num = cur_block->op2_to->start_opline - new_opcodes;
+               }
+               if(cur_block->ext_to) {
+                       cur_block->start_opline[cur_block->len-1].extended_value = cur_block->ext_to->start_opline - new_opcodes;
+               }
+               print_block(cur_block, new_opcodes, "Out ");
+       }
+       efree(op_array->opcodes);
+       op_array->opcodes = erealloc(new_opcodes, op_array->last*sizeof(zend_op));
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       /* adjust early binding list */
+       if (op_array->early_binding != -1) {
+               zend_uint *opline_num = &op_array->early_binding;
+               zend_op *end;
+
+               opline = op_array->opcodes;
+               end = opline + op_array->last;
+               while (opline < end) {
+                       if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
+                               *opline_num = opline - op_array->opcodes;
+                               opline_num = &ZEND_RESULT(opline).opline_num;
+                       }
+                       ++opline;
+               }
+               *opline_num = -1;
+       }
+#endif
+}
+
+static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks)
+{
+       /* last_op is the last opcode of the current block */
+       zend_op *last_op = (block->start_opline+block->len-1);
+
+       switch(last_op->opcode) {
+               case ZEND_JMP:
+                       {
+                               zend_op *target = block->op1_to->start_opline;
+                               zend_code_block *next = block->next;
+
+                               while (next && !next->access) {
+                                       /* find used one */
+                                       next = next->next;
+                               }
+
+                               /* JMP(next) -> NOP */
+                               if(block->op1_to == next) {
+                                       block->follow_to = block->op1_to;
+                                       block->op1_to = NULL;
+                                       MAKE_NOP(last_op);
+                                       block->len--;
+                                       if(block->len == 0) {
+                                               /* this block is nothing but NOP now */
+                                               delete_code_block(block);
+                                       }
+                                       break;
+                               }
+
+                               if(((target->opcode == ZEND_JMP &&
+                                    block->op1_to != block->op1_to->op1_to) ||
+                                   target->opcode == ZEND_JMPZNZ) &&
+                                  !block->op1_to->is_try) {
+                                       /* JMP L, L: JMP L1 -> JMP L1 */
+                                       /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
+                                       *last_op = *target;
+                                       if(ZEND_OP1_TYPE(last_op) == IS_CONST) {
+                                               zval_copy_ctor(&ZEND_OP1_LITERAL(last_op));
+                                       }
+                                       del_source(block, block->op1_to);
+                                       if (block->op1_to->op2_to) {
+                                               block->op2_to = block->op1_to->op2_to;
+                                               ADD_SOURCE(block, block->op2_to);
+                                       }
+                                       if (block->op1_to->ext_to) {
+                                               block->ext_to = block->op1_to->ext_to;
+                                               ADD_SOURCE(block, block->ext_to);
+                                       }
+                                       if (block->op1_to->op1_to) {
+                                               block->op1_to = block->op1_to->op1_to;
+                                               ADD_SOURCE(block, block->op1_to);
+                                       } else {
+                                               block->op1_to = NULL;
+                                       }
+                               } else if(target->opcode == ZEND_RETURN ||
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                         target->opcode == ZEND_RETURN_BY_REF ||
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                         target->opcode == ZEND_FAST_RET ||
+#endif
+                                     target->opcode == ZEND_EXIT) {
+                                       /* JMP L, L: RETURN to immediate RETURN */
+                                       *last_op = *target;
+                                       if(ZEND_OP1_TYPE(last_op) == IS_CONST) {
+                                               zval_copy_ctor(&ZEND_OP1_LITERAL(last_op));
+                                       }
+                                       del_source(block, block->op1_to);
+                                       block->op1_to = NULL;
+#if 0
+                               /* Temporarily disabled - see bug #0025274 */
+                               } else if (0&& block->op1_to != block &&
+                                  block->op1_to != blocks &&
+                                                  op_array->last_try_catch == 0 &&
+                                          target->opcode != ZEND_FREE &&
+                                          target->opcode != ZEND_SWITCH_FREE) {
+                                   /* Block Reordering (saves one JMP on each "for" loop iteration)
+                                    * It is disabled for some cases (ZEND_FREE/ZEND_SWITCH_FREE)
+                                    * which may break register allocation.
+                    */
+                                       zend_bool can_reorder = 0;
+                                       zend_block_source *cs = block->op1_to->sources;
+
+                                       /* the "target" block doesn't had any followed block */
+                                       while(cs) {
+                                               if (cs->from->follow_to == block->op1_to) {
+                                                       can_reorder = 0;
+                                                       break;
+                                               }
+                                               cs = cs->next;
+                                       }
+                                       if (can_reorder) {
+                                               next = block->op1_to;
+                                               /* the "target" block is not followed by current "block" */
+                                               while (next->follow_to != NULL) {
+                                                       if (next->follow_to == block) {
+                                                               can_reorder = 0;
+                                                               break;
+                                                       }
+                                                       next = next->follow_to;
+                                               }
+                                               if (can_reorder) {
+                                                       zend_code_block *prev = blocks;
+
+                                                       while (prev->next != block->op1_to) {
+                                                               prev = prev->next;
+                                                       }
+                                                       prev->next = next->next;
+                                                       next->next = block->next;
+                                                       block->next = block->op1_to;
+
+                                                       block->follow_to = block->op1_to;
+                                                       block->op1_to = NULL;
+                                                       MAKE_NOP(last_op);
+                                                       block->len--;
+                                                       if(block->len == 0) {
+                                                               /* this block is nothing but NOP now */
+                                                               delete_code_block(block);
+                                                       }
+                                                       break;
+                                               }
+                                       }
+#endif
+                               }
+                       }
+                       break;
+
+               case ZEND_JMPZ:
+               case ZEND_JMPNZ:
+                       /* constant conditional JMPs */
+                       if(ZEND_OP1_TYPE(last_op) == IS_CONST) {
+                               int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
+                               if (last_op->opcode == ZEND_JMPZ) {
+                                       should_jmp = !should_jmp;
+                               }
+                               literal_dtor(&ZEND_OP1_LITERAL(last_op));
+                               ZEND_OP1_TYPE(last_op) = IS_UNUSED;
+                               if (should_jmp) {
+                                       /* JMPNZ(true) -> JMP */
+                                       last_op->opcode = ZEND_JMP;
+                                       COPY_NODE(last_op->op1, last_op->op2);
+                                       block->op1_to = block->op2_to;
+                                       del_source(block, block->follow_to);
+                                       block->op2_to = NULL;
+                                       block->follow_to = NULL;
+                               } else {
+                                       /* JMPNZ(false) -> NOP */
+                                       MAKE_NOP(last_op);
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = NULL;
+                               }
+                               break;
+                       }
+
+                       if(block->op2_to) {
+                               zend_uchar same_type = ZEND_OP1_TYPE(last_op);
+                               zend_uint same_var = VAR_NUM_EX(last_op->op1);
+                               zend_op *target;
+                               zend_op *target_end;
+                               zend_code_block *target_block = block->op2_to;;
+
+next_target:
+                               target = target_block->start_opline;
+                               target_end = target_block->start_opline + target_block->len;
+                               while(target < target_end && target->opcode == ZEND_NOP) {
+                                       target++;
+                               }
+
+                               /* next block is only NOP's */
+                               if(target == target_end) {
+                                       target_block = target_block->follow_to;
+                                       goto next_target;
+                               } else
+                               /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
+                               if(target->opcode == INV_COND(last_op->opcode) &&
+                                  (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                  same_type == ZEND_OP1_TYPE(target) &&
+                                  same_var == VAR_NUM_EX(target->op1) &&
+                                  target_block->follow_to &&
+                                  !target_block->is_try
+                                  ) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->follow_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZ(X, L), L: X = JMPNZ_EX(X, L2) -> JMPZ(X, L+1) */
+                               else if(target->opcode == INV_COND_EX(last_op->opcode) &&
+                                       (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                   same_type == ZEND_OP1_TYPE(target) &&
+                                   same_var == VAR_NUM_EX(target->op1) &&
+                                       target_block->follow_to &&
+                                       !target_block->is_try
+                                       ) {
+                                       last_op->opcode += 3;
+                                       last_op->result = target->result;
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->follow_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
+                               else if(target_block->op2_to &&
+                                  target->opcode == last_op->opcode &&
+                                  (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                  same_type == ZEND_OP1_TYPE(target) &&
+                                  same_var == VAR_NUM_EX(target->op1) &&
+                                  !target_block->is_try
+                                  ) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op2_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
+                               else if(target_block->op1_to &&
+                                       target->opcode == ZEND_JMP &&
+                                       !target_block->is_try
+                                       ) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op1_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
+                               else if(target_block->op2_to &&
+                                               target_block->ext_to &&
+                                               target->opcode == ZEND_JMPZNZ &&
+                                               (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                       same_type == ZEND_OP1_TYPE(target) &&
+                                       same_var == VAR_NUM_EX(target->op1) &&
+                                               ! target_block->is_try
+                                               ) {
+                                       del_source(block, block->op2_to);
+                                       if (last_op->opcode == ZEND_JMPZ) {
+                                               block->op2_to = target_block->op2_to;
+                                       } else {
+                                               block->op2_to = target_block->ext_to;
+                                       }
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                       }
+
+                       if (block->follow_to &&
+                           (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
+                               zend_op *target;
+                               zend_op *target_end;
+
+                               while (1) {
+                                       target = block->follow_to->start_opline;
+                                       target_end = block->follow_to->start_opline + block->follow_to->len;
+                                       while (target < target_end && target->opcode == ZEND_NOP) {
+                                               target++;
+                                       }
+
+                                       /* next block is only NOP's */
+                                       if(target == target_end) {
+                                               del_source(block, block->follow_to);
+                                               block->follow_to = block->follow_to->follow_to;
+                                               ADD_SOURCE(block, block->follow_to);
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
+                               if (target->opcode == ZEND_JMP &&
+                                       block->follow_to->op1_to &&
+                                       !block->follow_to->is_try) {
+                                       del_source(block, block->follow_to);
+                                       if (last_op->opcode == ZEND_JMPZ) {
+                                               block->ext_to = block->follow_to->op1_to;
+                                               ADD_SOURCE(block, block->ext_to);
+                                       } else {
+                                               block->ext_to = block->op2_to;
+                                               block->op2_to = block->follow_to->op1_to;
+                                               ADD_SOURCE(block, block->op2_to);
+                                       }
+                                       block->follow_to = NULL;
+                                       last_op->opcode = ZEND_JMPZNZ;
+                               }
+                       }
+                       break;
+
+               case ZEND_JMPNZ_EX:
+               case ZEND_JMPZ_EX:
+                       /* constant conditional JMPs */
+                       if(ZEND_OP1_TYPE(last_op) == IS_CONST) {
+                               int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
+                               if (last_op->opcode == ZEND_JMPZ_EX) {
+                                       should_jmp = !should_jmp;
+                               }
+                               if (!should_jmp) {
+                                       /* T = JMPZ_EX(true,L)   -> T = QM_ASSIGN(true)
+                                        * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
+                                        */
+                                       last_op->opcode = ZEND_QM_ASSIGN;
+                                       SET_UNUSED(last_op->op2);
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = NULL;
+                               }
+                               break;
+                       }
+
+                       if(block->op2_to) {
+                               zend_op *target, *target_end;
+                               char *same_t=NULL;
+                               zend_code_block *target_block;
+                               int var_num = 0;
+                               if(op_array->T >= (zend_uint)op_array->last_var){
+                                       var_num = op_array->T;
+                               } else {
+                                       var_num = op_array->last_var;
+                               }
+                               if( var_num <= 0 ) {
+                                       return;
+                               }
+                               same_t = ecalloc(var_num, sizeof(char));
+                               if(same_t == NULL){
+                                       return;
+                               }
+                               same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
+                               same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
+                               target_block = block->op2_to;
+next_target_ex:
+                               target = target_block->start_opline;
+                               target_end = target_block->start_opline + target_block->len;
+                               while(target < target_end && target->opcode == ZEND_NOP) {
+                                       target++;
+                               }
+                               /* next block is only NOP's */
+                               if(target == target_end) {
+                                       target_block = target_block->follow_to;
+                                       goto next_target_ex;
+                               } else
+               /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
+                               if(target_block->op2_to &&
+                                  target->opcode == last_op->opcode-3 &&
+                                  (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                  (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
+                                  !target_block->is_try
+                                  ) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op2_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                                       /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
+                               } else if(target_block->op2_to &&
+                                                 target->opcode == INV_EX_COND(last_op->opcode) &&
+                                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                                 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
+                                                 !target_block->is_try
+                                                 ) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->follow_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                                       /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
+                               } else if(target_block->op2_to &&
+                                                 target->opcode == INV_EX_COND_EX(last_op->opcode) &&
+                                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                                 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
+                                                 (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
+                                                 !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->follow_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
+                               } else if(target_block->op2_to &&
+                                                 target->opcode == last_op->opcode &&
+                                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                                 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
+                                                 (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
+                                                 !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op2_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
+                               } else if(target_block->op1_to &&
+                                       target->opcode == ZEND_JMP &&
+                                       !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op1_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
+                               } else if(target_block->op2_to &&
+                                                 target_block->ext_to &&
+                                                 target->opcode == ZEND_JMPZNZ &&
+                                                 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                                 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
+                                                 !target_block->is_try
+                                                 ) {
+                                       del_source(block, block->op2_to);
+                                       if (last_op->opcode == ZEND_JMPZ_EX) {
+                                               block->op2_to = target_block->op2_to;
+                                       } else {
+                                               block->op2_to = target_block->ext_to;
+                                       }
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               if(same_t != NULL){
+                                       efree(same_t);
+                               }
+                       }
+                       break;
+
+               case ZEND_JMPZNZ: {
+                       zend_code_block *next = block->next;
+
+                       while (next && !next->access) {
+                               /* find first accessed one */
+                               next = next->next;
+                       }
+
+                       if(ZEND_OP1_TYPE(last_op) == IS_CONST) {
+                               if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
+                                       /* JMPZNZ(false,L1,L2) -> JMP(L1) */
+                                       zend_code_block *todel;
+                                       
+                                       literal_dtor(&ZEND_OP1_LITERAL(last_op));
+                                       last_op->opcode = ZEND_JMP;
+                                       SET_UNUSED(last_op->op1);
+                                       SET_UNUSED(last_op->op2);
+                                       block->op1_to = block->op2_to;
+                                       todel = block->ext_to;
+                                       block->op2_to = NULL;
+                                       block->ext_to = NULL;
+                                       del_source(block, todel);
+                               } else {
+                                       /* JMPZNZ(true,L1,L2) -> JMP(L2) */
+                                       zend_code_block *todel;
+                  
+                                       literal_dtor(&ZEND_OP1_LITERAL(last_op));
+                                       last_op->opcode = ZEND_JMP;
+                                       SET_UNUSED(last_op->op1);
+                                       SET_UNUSED(last_op->op2);
+                                       block->op1_to = block->ext_to;
+                                       todel =  block->op2_to;
+                                       block->op2_to = NULL;
+                                       block->ext_to = NULL;
+                                       del_source(block, todel);
+                               }
+                       } else if (block->op2_to == block->ext_to) {
+                               /* both goto the same one - it's JMP */
+                               /* JMPZNZ(?,L,L) -> JMP(L) */
+                               last_op->opcode = ZEND_JMP;
+                               SET_UNUSED(last_op->op1);
+                               SET_UNUSED(last_op->op2);
+                               block->op1_to = block->op2_to;
+                               block->op2_to = NULL;
+                               block->ext_to = NULL;
+                       } else if (block->op2_to == next) {
+                               /* jumping to next on Z - can follow to it and jump only on NZ */
+                               /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
+                               last_op->opcode = ZEND_JMPNZ;
+                               block->op2_to = block->ext_to;
+                               block->follow_to = next;
+                               block->ext_to = NULL;
+                               /* no need to add source - it's block->op2_to */
+                       } else if (block->ext_to == next) {
+                               /* jumping to next on NZ - can follow to it and jump only on Z */
+                               /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
+                               last_op->opcode = ZEND_JMPZ;
+                               block->follow_to = next;
+                               block->ext_to = NULL;
+                               /* no need to add source - it's block->ext_to */
+                       }
+
+                       if(last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
+                               zend_uchar same_type = ZEND_OP1_TYPE(last_op);
+                               zend_uchar same_var = VAR_NUM_EX(last_op->op1);
+                               zend_op *target;
+                               zend_op *target_end;
+                               zend_code_block *target_block = block->op2_to;
+
+next_target_znz:
+                               target = target_block->start_opline;
+                               target_end = target_block->start_opline + target_block->len;
+                               while(target < target_end && target->opcode == ZEND_NOP) {
+                                       target++;
+                               }
+                               /* next block is only NOP's */
+                               if(target == target_end) {
+                                       target_block = target_block->follow_to;
+                                       goto next_target_znz;
+                               /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+                               } else if(target_block->op2_to &&
+                                                 (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+                                                 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                                 same_type == ZEND_OP1_TYPE(target) &&
+                                                 same_var == VAR_NUM_EX(target->op1) &&
+                                                 !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op2_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+                               else if(target->opcode == ZEND_JMPNZ &&
+                                               (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+                                               same_type == ZEND_OP1_TYPE(target) &&
+                                               same_var == VAR_NUM_EX(target->op1) &&
+                                               target_block->follow_to &&
+                                               !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->follow_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                               /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
+                               else if(target_block->op1_to &&
+                                       target->opcode == ZEND_JMP &&
+                                       !target_block->is_try) {
+                                       del_source(block, block->op2_to);
+                                       block->op2_to = target_block->op1_to;
+                                       ADD_SOURCE(block, block->op2_to);
+                               }
+                       }
+                       break;
+               }
+       }
+}
+
+/* Global data dependencies */
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+
+# define T_USAGE(op) do { \
+               if((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
+                  !defined_here[VAR_NUM(op.var)] && !used_ext[VAR_NUM(op.var)]) {      \
+                       used_ext[VAR_NUM(op.var)] = 1;                                                                  \
+               } \
+       } while (0)
+
+# define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !usage[VAR_NUM(op.var)]) /* !used_ext[op.var] && */
+# define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
+
+#else
+
+# define T_USAGE(op) do { \
+               if((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && \
+                  !defined_here[VAR_NUM(op.u.var)] && !used_ext[VAR_NUM(op.u.var)]) {  \
+                       used_ext[VAR_NUM(op.u.var)] = 1;                                                                        \
+               } \
+       } while (0)
+
+# define NEVER_USED(op) ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && !usage[VAR_NUM(op.u.var)]) /* !used_ext[op.u.var] && */
+# define RES_NEVER_USED(opline) (ZEND_RESULT_TYPE(opline) == IS_UNUSED || NEVER_USED(opline->result))
+
+#endif
+
+/* Find a set of variables which are used outside of the block where they are
+ * defined. We won't apply some optimization patterns for sush variables. */
+static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, char *used_ext)
+{
+       zend_code_block *next_block = block->next;
+       char *usage;
+       char *defined_here;
+
+       if(op_array->T == 0) {
+               /* shortcut - if no Ts, nothing to do */
+               return;
+       }
+
+       usage = ecalloc(op_array->T, 1);
+       defined_here = emalloc(op_array->T);
+
+       while(next_block) {
+               zend_op *opline = next_block->start_opline;
+               zend_op *end = opline+next_block->len;
+
+               if(!next_block->access) {
+                       next_block = next_block->next;
+                       continue;
+               }
+               memset(defined_here, 0, op_array->T);
+
+               while(opline<end) {
+                       T_USAGE(opline->op1);
+                       T_USAGE(opline->op2);
+
+                       if(RESULT_USED(opline)) {
+                               if(!defined_here[VAR_NUM(ZEND_RESULT(opline).var)] && !used_ext[VAR_NUM(ZEND_RESULT(opline).var)] &&
+                                  (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT ||
+                                       (opline->opcode == ZEND_OP_DATA && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR) ||
+                                       opline->opcode == ZEND_ADD_ARRAY_ELEMENT)) {
+                                       /* these opcodes use the result as argument */
+                                       used_ext[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
+                               }
+                               defined_here[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
+                       }
+                       opline++;
+               }
+               next_block = next_block->next;
+       }
+
+#if DEBUG_BLOCKPASS
+       {
+               int i;
+               for(i=0; i< op_array->T; i++) {
+                       fprintf(stderr, "T%d: %c\n", i, used_ext[i]+'0');
+               }
+       }
+#endif
+
+       while(block) {
+               zend_op *opline = block->start_opline+block->len-1;
+
+               if(!block->access) {
+                       block = block->next;
+                       continue;
+               }
+
+               memcpy(usage, used_ext, op_array->T);
+
+               while(opline>=block->start_opline) {
+                       /* usage checks */
+                       if(RES_NEVER_USED(opline)) {
+                               switch(opline->opcode) {
+                                       case ZEND_ASSIGN_ADD:
+                                       case ZEND_ASSIGN_SUB:
+                                       case ZEND_ASSIGN_MUL:
+                                       case ZEND_ASSIGN_DIV:
+                                       case ZEND_ASSIGN_MOD:
+                                       case ZEND_ASSIGN_SL:
+                                       case ZEND_ASSIGN_SR:
+                                       case ZEND_ASSIGN_CONCAT:
+                                       case ZEND_ASSIGN_BW_OR:
+                                       case ZEND_ASSIGN_BW_AND:
+                                       case ZEND_ASSIGN_BW_XOR:
+                                       case ZEND_PRE_INC:
+                                       case ZEND_PRE_DEC:
+                                       case ZEND_POST_INC:
+                                       case ZEND_POST_DEC:
+                                       case ZEND_ASSIGN:
+                                       case ZEND_ASSIGN_REF:
+                                       case ZEND_DO_FCALL:
+                                       case ZEND_DO_FCALL_BY_NAME:
+                                               if(ZEND_RESULT_TYPE(opline) == IS_VAR) {
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                                       ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
+#else
+                                                       ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED;
+#endif
+                                               }
+                                               break;
+                                       case ZEND_QM_ASSIGN:
+                                       case ZEND_BOOL:
+                                       case ZEND_BOOL_NOT:
+                                               if(ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                               }
+                                               MAKE_NOP(opline);
+                                               break;
+                                       case ZEND_PRINT:
+                                               opline->opcode = ZEND_ECHO;
+                                               ZEND_RESULT_TYPE(opline) = IS_UNUSED;
+                                               break;
+                                       case ZEND_JMPZ_EX:
+                                       case ZEND_JMPNZ_EX:
+                                               opline->opcode -= 3;
+                                               SET_UNUSED(opline->result);
+                                               break;
+                               }
+                       }
+
+                       if(opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT ||
+                          opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
+                               if(ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
+                                       usage[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
+                               }
+                       } else {
+                               if(RESULT_USED(opline)) {
+                                       usage[VAR_NUM(ZEND_RESULT(opline).var)] = 0;
+                               }
+                       }
+
+                       if(ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
+                               usage[VAR_NUM(ZEND_OP1(opline).var)] = 1;
+                       }
+                       if(ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
+                               usage[VAR_NUM(ZEND_OP2(opline).var)] = 1;
+                       }
+
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       if((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
+                          (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
+                          usage[VAR_NUM(ZEND_RESULT(opline).var)]) {
+                               ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
+                       }
+#else
+                       if(ZEND_RESULT_TYPE(opline) == IS_VAR &&
+                          usage[VAR_NUM(ZEND_RESULT(opline).var)] &&
+                          (ZEND_RESULT(opline).EA.type & EXT_TYPE_UNUSED) != 0) {
+                               ZEND_RESULT(opline).EA.type &= ~EXT_TYPE_UNUSED;
+                       }
+#endif
+
+                       opline--;
+               }
+               block = block->next;
+       } /* end blocks */
+       efree(defined_here);
+       efree(usage);
+}
+
+#define PASSES 3
+
+static void zend_block_optimization(zend_op_array *op_array TSRMLS_DC)
+{
+       zend_code_block *blocks, *cur_block;
+       int pass;
+       char *usage;
+
+#if DEBUG_BLOCKPASS
+       fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name?op_array->function_name:"main");
+       fflush(stderr);
+#endif
+
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+       if (op_array->has_finally_block) {
+               return;
+       }
+#endif
+
+    /* Build CFG */
+       blocks = find_code_blocks(op_array);
+       if(!blocks) {
+               return;
+       }
+
+       zend_rebuild_access_path(blocks, op_array, 0);
+       /* full rebuild here to produce correct sources! */
+       usage = emalloc(op_array->T);
+       for(pass=0; pass< PASSES; pass++) {
+               /* Compute data dependencies */
+               memset(usage, 0, op_array->T);
+               zend_t_usage(blocks, op_array, usage);
+
+               /* optimize each basic block separately */
+               for(cur_block = blocks; cur_block; cur_block = cur_block->next) {
+                       if(!cur_block->access) {
+                               continue;
+                       }
+                       zend_optimize_block(cur_block, op_array, usage TSRMLS_CC);
+               }
+
+               /* Jump optimization for each block */
+               for(cur_block = blocks; cur_block; cur_block = cur_block->next) {
+                       if(!cur_block->access) {
+                               continue;
+                       }
+                       zend_jmp_optimization(cur_block, op_array, blocks);
+               }
+
+               /* Eliminate unreachable basic blocks */
+               zend_rebuild_access_path(blocks, op_array, 1);
+       }
+
+       assemble_code_blocks(blocks, op_array);
+       efree(usage);
+
+       /* Destroy CFG */
+       for(cur_block = blocks; cur_block; cur_block = cur_block->next) {
+               zend_block_source *cs = cur_block->sources;
+               while(cs) {
+                       zend_block_source *n = cs->next;
+                       efree(cs);
+                       cs = n;
+               }
+       }
+       efree(blocks);
+}
diff --git a/Optimizer/nop_removal.c b/Optimizer/nop_removal.c
new file mode 100644 (file)
index 0000000..49ffe0e
--- /dev/null
@@ -0,0 +1,126 @@
+/* pass 10:
+ * - remove NOPs
+ */
+
+static void nop_removal(zend_op_array *op_array)
+{
+       zend_op *end, *opline;
+       zend_uint new_count, i, shift;
+       int j;
+       zend_uint *shiftlist;
+       ALLOCA_FLAG(use_heap);
+
+       shiftlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint) * op_array->last);
+       i = new_count = shift = 0;
+       end = op_array->opcodes+op_array->last;
+       for (opline = op_array->opcodes; opline < end; opline++) {
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               /* GOTO target is unresolved yet. We can't optimize. */
+               if (opline->opcode == ZEND_GOTO &&
+                       Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
+                       /* TODO: in general we can avoid this restriction */
+                       FREE_ALLOCA(shiftlist);
+                       return;
+               }
+#endif
+
+               /* Kill JMP-over-NOP-s */
+               if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
+                       /* check if there are only NOPs under the branch */
+                       zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1;
+
+                       while (target->opcode == ZEND_NOP) {
+                               target--;
+                       }
+                       if (target == opline) {
+                               /* only NOPs */
+                               opline->opcode = ZEND_NOP;
+                       }
+               }
+
+               shiftlist[i++] = shift;
+               if (opline->opcode == ZEND_NOP) {
+                       shift++;
+               } else {
+                       if (shift) {
+                               op_array->opcodes[new_count] = *opline;
+                       }
+                       new_count++;
+               }
+       }
+
+       if (shift) {
+               op_array->last = new_count;
+               end = op_array->opcodes + op_array->last;
+
+               /* update JMPs */
+               for (opline = op_array->opcodes; opline<end; opline++) {
+                       switch (opline->opcode) {
+                               case ZEND_JMP:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                               case ZEND_GOTO:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               case ZEND_FAST_CALL:
+#endif
+                                       ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num];
+                                       break;
+                               case ZEND_JMPZ:
+                               case ZEND_JMPNZ:
+                               case ZEND_JMPZ_EX:
+                               case ZEND_JMPNZ_EX:
+                               case ZEND_FE_FETCH:
+                               case ZEND_FE_RESET:
+                               case ZEND_NEW:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                               case ZEND_JMP_SET:
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               case ZEND_JMP_SET_VAR:
+#endif
+                                       ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
+                                       break;
+                               case ZEND_JMPZNZ:
+                                       ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
+                                       opline->extended_value -= shiftlist[opline->extended_value];
+                                       break;
+                               case ZEND_CATCH:
+                                       opline->extended_value -= shiftlist[opline->extended_value];
+                                       break;
+                       }
+               }
+
+               /* update brk/cont array */
+               for (i=0; i<op_array->last_brk_cont; i++) {
+                       op_array->brk_cont_array[i].brk -= shiftlist[op_array->brk_cont_array[i].brk];
+                       op_array->brk_cont_array[i].cont -= shiftlist[op_array->brk_cont_array[i].cont];
+                       op_array->brk_cont_array[i].start -= shiftlist[op_array->brk_cont_array[i].start];
+               }
+
+               /* update try/catch array */
+               for (j=0; j<op_array->last_try_catch; j++) {
+                       op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
+                       op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                       if (op_array->try_catch_array[j].finally_op) {
+                               op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+                               op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+                       }
+#endif
+               }
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               /* update early binding list */
+               if (op_array->early_binding != -1) {
+                       zend_uint *opline_num = &op_array->early_binding;
+
+                       do {
+                               *opline_num -= shiftlist[*opline_num];
+                               opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
+                       } while (*opline_num != -1);
+               }
+#endif
+       }
+       FREE_ALLOCA(shiftlist);
+}
diff --git a/Optimizer/optimize_temp_vars_5.c b/Optimizer/optimize_temp_vars_5.c
new file mode 100644 (file)
index 0000000..4e2865e
--- /dev/null
@@ -0,0 +1,222 @@
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+
+/* ops that use CLs:
+op1:
+ZEND_FETCH_CONSTANT:
+ZEND_INIT_CTOR_CALL:
+ZEND_INIT_STATIC_METHOD_CALL:
+ZEND_INIT_METHOD_CALL:
+ZEND_IMPORT_CLASS:
+ZEND_IMPORT_FUNCTION:
+ZEND_IMPORT_CONST:
+ZEND_ADD_INTERFACE:
+ZEND_VERIFY_ABSTRACT_CLASS:
+ZEND_NEW:
+ZEND_CATCH:
+ZEND_INIT_FCALL_BY_NAME:
+
+op2:
+ZEND_UNSET_VAR:
+ZEND_ISSET_ISEMPTY_VAR:
+ZEND_FETCH_UNSET:
+ZEND_FETCH_IS:
+ZEND_FETCH_R:
+ZEND_FETCH_W:
+ZEND_FETCH_RW:
+ZEND_FETCH_FUNC_ARG:
+ZEND_ADD_INTERFACE:
+ZEND_INSTANCEOF:
+
+extended_value:
+ZEND_DECLARE_INHERITED_CLASS:
+
+ignore result
+INIT_METHOD_CALL:
+*/
+
+#define OP1_CONST_IS_CLASS 1
+#define OP2_CONST_IS_CLASS 2
+#define EXT_CONST_IS_CLASS 4
+#define RESULT_IS_UNUSED   8
+
+static const char op_const_means_class[256]  = {
+       /* 0 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       /* 32 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+       /* 64 */
+       0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2,
+       /* 96 */
+       0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       /* 128 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       /* 160 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       /* 192 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       /* 224 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+#define GET_AVAILABLE_T()              \
+       for (i=0; i<T; i++) {           \
+               if (!taken_T[i]) {              \
+                       break;                          \
+               }                                               \
+       }                                                       \
+       taken_T[i] = 1;                         \
+       if (i > max) {                          \
+               max = i;                                \
+       }
+
+static void optimize_temporary_variables(zend_op_array *op_array)
+{
+       int T = op_array->T;
+       char *taken_T;                  /* T index in use */
+       zend_op **start_of_T;   /* opline where T is first used */
+       char *valid_T;                  /* Is the map_T valid */
+       int *map_T;                             /* Map's the T to its new index */
+       zend_op *opline, *end;
+       int currT;
+       int i;
+       int max = -1;
+       int var_to_free = -1;
+
+       taken_T = (char *) emalloc(T);
+       start_of_T = (zend_op **) emalloc(T*sizeof(zend_op *));
+       valid_T = (char *) emalloc(T);
+       map_T = (int *) emalloc(T*sizeof(int));
+
+    end = op_array->opcodes;
+    opline = &op_array->opcodes[op_array->last-1];
+
+    /* Find T definition points */
+    while (opline >= end) {
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
+                       if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
+                               start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
+                       }
+               }
+#else
+        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
+                       start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
+               }
+#endif
+               opline--;
+       }
+
+       memset(valid_T, 0, T);
+       memset(taken_T, 0, T);
+
+    end = op_array->opcodes;
+    opline = &op_array->opcodes[op_array->last-1];
+
+    while (opline >= end) {
+               if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+                       || ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST)
+#endif
+                       ) {
+                       currT = VAR_NUM(ZEND_OP1(opline).var);
+                       if (!valid_T[currT]) {
+                               GET_AVAILABLE_T();
+                               map_T[currT] = i;
+                               valid_T[currT] = 1;
+                       }
+                       ZEND_OP1(opline).var = NUM_VAR(map_T[currT]);
+               }
+
+               /* Skip OP_DATA */
+               if (opline->opcode == ZEND_OP_DATA && 
+                   (opline-1)->opcode == ZEND_ASSIGN_DIM) {
+                   opline--;
+                   continue;
+               }
+
+               if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+                       || ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST)
+#endif
+                  ) {
+                       currT = VAR_NUM(ZEND_OP2(opline).var);
+                       if (!valid_T[currT]) {
+                               GET_AVAILABLE_T();
+                               map_T[currT] = i;
+                               valid_T[currT] = 1;
+                       }
+                       ZEND_OP2(opline).var = NUM_VAR(map_T[currT]);
+               }
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+               if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) {
+#else
+               if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
+            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
+#endif
+                       currT = VAR_NUM(opline->extended_value);
+                       if (!valid_T[currT]) {
+                               GET_AVAILABLE_T();
+                               map_T[currT] = i;
+                               valid_T[currT] = 1;
+                       }
+                       opline->extended_value = NUM_VAR(map_T[currT]);
+               }
+
+               /* Allocate OP_DATA->op2 after "opernds", but before "result" */
+               if (opline->opcode == ZEND_ASSIGN_DIM &&
+                   (opline+1)->opcode == ZEND_OP_DATA &&
+                   ZEND_OP2_TYPE(opline+1) & (IS_VAR | IS_TMP_VAR)) {
+                       currT = VAR_NUM(ZEND_OP2(opline+1).var);
+                       GET_AVAILABLE_T();
+                       map_T[currT] = i;
+                       valid_T[currT] = 1;
+                       taken_T[i] = 0;
+                       ZEND_OP2(opline+1).var = NUM_VAR(i);
+                       var_to_free = i;
+               }
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+               if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
+                       if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
+#else
+               if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
+#endif
+                               currT = VAR_NUM(ZEND_RESULT(opline).var);
+                               if (valid_T[currT]) {
+                                       if (start_of_T[currT] == opline) {
+                                               taken_T[map_T[currT]] = 0;
+                                       }
+                                       ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]);
+                               } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
+                                       GET_AVAILABLE_T();
+
+                                       if (RESULT_UNUSED(opline)) {
+                                               taken_T[i] = 0;
+                                       } else {
+                                               /* Code which gets here is using a wrongly built opcode such as RECV() */
+                                               map_T[currT] = i;
+                                               valid_T[currT] = 1;
+                                       }
+                                       ZEND_RESULT(opline).var = NUM_VAR(i);
+                               }
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+                       }
+#endif
+               }
+
+               if (var_to_free >= 0) {
+                       taken_T[var_to_free] = 0;
+                       var_to_free = -1;
+               }
+
+               opline--;
+       }
+
+       efree(taken_T);
+       efree(start_of_T);
+       efree(valid_T);
+       efree(map_T);
+       op_array->T = max+1;
+}
diff --git a/Optimizer/pass10.c b/Optimizer/pass10.c
new file mode 100644 (file)
index 0000000..3bfcec6
--- /dev/null
@@ -0,0 +1,3 @@
+if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) {
+       nop_removal(op_array);
+}
diff --git a/Optimizer/pass1_5.c b/Optimizer/pass1_5.c
new file mode 100644 (file)
index 0000000..247b1b8
--- /dev/null
@@ -0,0 +1,391 @@
+/* pass 1
+ * - substitute persistent constants (true, false, null, etc)
+ * - perform compile-time evaluation of constant binary and unary operations
+ * - optimize series of ADD_STRING and/or ADD_CHAR
+ * - convert CAST(IS_BOOL,x) into BOOL(x)
+ * - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL
+ */
+
+if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
+       int i=0;
+       zend_op *opline = op_array->opcodes;
+       zend_op *end = opline + op_array->last;
+
+       while (opline<end) {
+               switch (opline->opcode) {
+               case ZEND_ADD:
+               case ZEND_SUB:
+               case ZEND_MUL:
+               case ZEND_DIV:
+               case ZEND_MOD:
+               case ZEND_SL:
+               case ZEND_SR:
+               case ZEND_CONCAT:
+               case ZEND_IS_EQUAL:
+               case ZEND_IS_NOT_EQUAL:
+               case ZEND_IS_SMALLER:
+               case ZEND_IS_SMALLER_OR_EQUAL:
+               case ZEND_IS_IDENTICAL:
+               case ZEND_IS_NOT_IDENTICAL:
+               case ZEND_BW_OR:
+               case ZEND_BW_AND:
+               case ZEND_BW_XOR:
+               case ZEND_BOOL_XOR:
+                       if (ZEND_OP1_TYPE(opline)==IS_CONST
+                               && ZEND_OP2_TYPE(opline)==IS_CONST) {
+                               /* binary operation wheit constant operands */
+                               int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode);
+                               zend_uint tv = ZEND_RESULT(opline).var;         /* temporary variable */
+                               zval result;
+                               zend_op *tmp_opline;
+                               int er;
+
+                               if (opline->opcode == ZEND_DIV && 
+                                   Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG &&
+                                   Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) {
+                                       /* div by 0 */
+                                       break;
+                               }
+                               er = EG(error_reporting);
+                               EG(error_reporting) = 0;
+                               /* evaluate constant expression */
+                               if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) != SUCCESS) {
+                                       EG(error_reporting) = er;
+                                       break;
+                               }
+                               EG(error_reporting) = er;
+                               PZ_SET_REFCOUNT_P(&result, 1);
+                               PZ_UNSET_ISREF_P(&result);
+
+                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                               literal_dtor(&ZEND_OP2_LITERAL(opline));
+                               MAKE_NOP(opline);
+
+                               /* substitute the following TMP_VAR usage with constant */
+                               for (tmp_opline=opline+1; tmp_opline<end; tmp_opline++) {
+                                       if (ZEND_OP1_TYPE(tmp_opline)== IS_TMP_VAR
+                                               && ZEND_OP1(tmp_opline).var == tv) {
+                                               if (tmp_opline->opcode==ZEND_FREE) {
+                                                       MAKE_NOP(tmp_opline);
+                                                       zval_dtor(&result);
+                                               } else {
+                                                       ZEND_OP1_TYPE(tmp_opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                                       tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC);
+                                                       if (Z_TYPE(result) == IS_STRING) {
+                                                               Z_HASH_P(&ZEND_OP1_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP1_LITERAL(tmp_opline))+1);
+                                                               if (tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
+                                                                   tmp_opline->opcode == ZEND_DO_FCALL ||
+                                                                   tmp_opline->opcode == ZEND_CATCH ||
+                                                                   tmp_opline->opcode == ZEND_FETCH_CONSTANT) {
+                                                                       op_array->literals[tmp_opline->op1.constant].cache_slot = op_array->last_cache_slot++;
+                                                               }
+                                                       }
+#else
+                                                       ZEND_OP1_LITERAL(tmp_opline) = result;
+#endif
+                                               }
+                                               /* TMP_VAR my be used only once */
+                                               break;
+                                       }
+                                       if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR
+                                               && ZEND_OP2(tmp_opline).var == tv) {
+                                               ZEND_OP2_TYPE(tmp_opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                               tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC);
+                                               if (Z_TYPE(result) == IS_STRING) {
+                                                       Z_HASH_P(&ZEND_OP2_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP2_LITERAL(tmp_opline))+1);
+                                                       if (tmp_opline->opcode == ZEND_FETCH_R ||
+                                                           tmp_opline->opcode == ZEND_FETCH_W ||
+                                                           tmp_opline->opcode == ZEND_FETCH_RW ||
+                                                           tmp_opline->opcode == ZEND_FETCH_IS ||
+                                                           tmp_opline->opcode == ZEND_FETCH_UNSET ||
+                                                           tmp_opline->opcode == ZEND_FETCH_FUNC_ARG ||
+                                                           tmp_opline->opcode == ZEND_FETCH_CLASS ||
+                                                           tmp_opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
+                                                           tmp_opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
+                                                           tmp_opline->opcode == ZEND_UNSET_VAR ||
+                                                           tmp_opline->opcode == ZEND_ISSET_ISEMPTY_VAR ||
+                                                           tmp_opline->opcode == ZEND_ADD_INTERFACE ||
+                                                           tmp_opline->opcode == ZEND_ADD_TRAIT) {
+                                                               op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot++;
+                                                       } else if (tmp_opline->opcode == ZEND_INIT_METHOD_CALL ||
+                                                                  tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_CONSTANT ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_R ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_W ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_RW ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_IS ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_UNSET ||
+                                                                  tmp_opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG ||
+                                                                  tmp_opline->opcode == ZEND_UNSET_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_PRE_INC_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_PRE_DEC_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_POST_INC_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_POST_DEC_OBJ ||
+                                                                  tmp_opline->opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ) {
+                                                               op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot;
+                                                               op_array->last_cache_slot += 2;
+                                                       } else if (tmp_opline->opcode == ZEND_ASSIGN_ADD ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_SUB ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_MUL ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_DIV ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_MOD ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_SL ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_SR ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_CONCAT ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_BW_OR ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_BW_AND ||
+                                                                  tmp_opline->opcode == ZEND_ASSIGN_BW_XOR) {
+                                                               if (tmp_opline->extended_value == ZEND_ASSIGN_OBJ) {
+                                                                       op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot;
+                                                                       op_array->last_cache_slot += 2;
+                                                               }
+                                                       }
+                                               }
+#else
+                                               ZEND_OP2_LITERAL(tmp_opline) = result;
+#endif
+                                               break;
+                                       }
+                               }
+                       }
+                       break;
+
+               case ZEND_CAST:
+                       if (ZEND_OP1_TYPE(opline)==IS_CONST &&
+                               opline->extended_value != IS_ARRAY &&
+                               opline->extended_value != IS_OBJECT &&
+                               opline->extended_value != IS_RESOURCE) {
+                               /* cast of constant operand */
+                               zval res;
+                               res = ZEND_OP1_LITERAL(opline);
+                               zval_copy_ctor(&res);
+                               switch (opline->extended_value) {
+                                       case IS_NULL:
+                                               convert_to_null(&res);
+                                               break;
+                                       case IS_BOOL:
+                                               convert_to_boolean(&res);
+                                               break;
+                                       case IS_LONG:
+                                               convert_to_long(&res);
+                                               break;
+                                       case IS_DOUBLE:
+                                               convert_to_double(&res);
+                                               break;
+                                       case IS_STRING:
+                                               convert_to_string(&res);
+                                               break;
+                               }
+                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                               opline->opcode = ZEND_QM_ASSIGN;
+                               opline->extended_value = 0;
+                               ZEND_OP1_LITERAL(opline) = res;
+                               SET_UNUSED(opline->op2);
+                       } else if (opline->extended_value == IS_BOOL) {
+                               /* T = CAST(X, IS_BOOL) => T = BOOL(X) */
+                               opline->opcode = ZEND_BOOL;
+                               opline->extended_value = 0;
+                       }
+                       break;
+
+               case ZEND_BW_NOT:
+               case ZEND_BOOL_NOT:
+                       if (ZEND_OP1_TYPE(opline)==IS_CONST) {
+                               /* unary operation on constant operand */
+                               unary_op_type unary_op = get_unary_op(opline->opcode);
+                               zval result;
+                               zend_op *tmp_opline;
+                               zend_uint tv = ZEND_RESULT(opline).var;         /* temporary variable */
+                               int er;
+
+                               er = EG(error_reporting);
+                               EG(error_reporting) = 0;
+                               if (unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC) != SUCCESS) {
+                                       EG(error_reporting) = er;
+                                       break;
+                               }
+                               EG(error_reporting) = er;
+                               PZ_SET_REFCOUNT_P(&result, 1);
+                               PZ_UNSET_ISREF_P(&result);
+
+                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                               MAKE_NOP(opline);
+
+                               /* substitute the following TMP_VAR usage with constant */
+                               for (tmp_opline=opline+1; tmp_opline<end; tmp_opline++) {
+                                       if (ZEND_OP1_TYPE(tmp_opline)== IS_TMP_VAR
+                                       && ZEND_OP1(tmp_opline).var == tv) {
+                                               if (tmp_opline->opcode==ZEND_FREE) {
+                                                       MAKE_NOP(tmp_opline);
+                                                       zval_dtor(&result);
+                                               } else {
+                                                       ZEND_OP1_TYPE(tmp_opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                                       tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC);
+#else
+                                                       ZEND_OP1_LITERAL(tmp_opline) = result;
+#endif
+                                               }
+                                               break;
+                                       }
+                                       if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR
+                                               && ZEND_OP2(tmp_opline).var == tv) {
+                                               ZEND_OP2_TYPE(tmp_opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                               tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC);
+#else
+                                               ZEND_OP2_LITERAL(tmp_opline) = result;
+#endif
+                                               break;
+                                       }
+                               }
+                       }
+                       break;
+
+               case ZEND_ADD_STRING:
+               case ZEND_ADD_CHAR:
+                       {
+                               zend_op *next_op = opline+1;
+                               int requires_conversion = (opline->opcode==ZEND_ADD_CHAR?1:0);
+                               size_t final_length = 0;
+                               char *ptr;
+                               zend_op *last_op;
+
+                               /* There is always a ZEND_RETURN at the end
+                               if (next_op>=end) {
+                               break;
+                               }
+                               */
+                               while (next_op->opcode == ZEND_ADD_STRING || next_op->opcode == ZEND_ADD_CHAR) {
+                                       if (ZEND_RESULT(opline).var != ZEND_RESULT(next_op).var) {
+                                               break;
+                                       }
+                                       if (next_op->opcode == ZEND_ADD_CHAR) {
+                                               final_length += 1;
+                                       } else { /* ZEND_ADD_STRING */
+                                               final_length += ZEND_OP2_LITERAL(next_op).value.str.len;
+                                       }
+                                       next_op++;
+                               }
+                               if (final_length == 0) {
+                                       break;
+                               }
+                               last_op = next_op;
+                               final_length += (requires_conversion ? 1 : ZEND_OP2_LITERAL(opline).value.str.len);
+                               ptr = (char *) emalloc(final_length+1);
+                               ptr[final_length] = '\0';
+                               if (requires_conversion) { /* ZEND_ADD_CHAR */
+                                       char chval = (char)ZEND_OP2_LITERAL(opline).value.lval;
+
+                                       ZEND_OP2_LITERAL(opline).value.str.val = ptr;
+                                       ptr[0] = chval;
+                                       ZEND_OP2_LITERAL(opline).type = IS_STRING;
+                                       opline->opcode = ZEND_ADD_STRING;
+                                       ptr++;
+                               } else { /* ZEND_ADD_STRING */
+                                       memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
+                                       if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) {
+                                               efree(Z_STRVAL(ZEND_OP2_LITERAL(opline)));
+                                       }
+                                       Z_STRVAL(ZEND_OP2_LITERAL(opline)) = ptr;
+                                       ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline));
+                               }
+                               ZEND_OP2_LITERAL(opline).value.str.len = final_length;
+                               next_op = opline+1;
+                               while (next_op < last_op) {
+                                       if (next_op->opcode == ZEND_ADD_STRING) {
+                                               memcpy(ptr, ZEND_OP2_LITERAL(next_op).value.str.val, ZEND_OP2_LITERAL(next_op).value.str.len);
+                                               ptr += ZEND_OP2_LITERAL(next_op).value.str.len;
+                                               literal_dtor(&ZEND_OP2_LITERAL(next_op));
+                                       } else { /* ZEND_ADD_CHAR */
+                                               *ptr = (char)ZEND_OP2_LITERAL(next_op).value.lval;
+                                               ptr++;
+                                       }
+                                       MAKE_NOP(next_op);
+                                       next_op++;
+                               }
+                               if (!((ZEND_OPTIMIZER_PASS_5|ZEND_OPTIMIZER_PASS_10) & OPTIMIZATION_LEVEL)) {
+                                       /* NOP removel is disabled => insert JMP over NOPs */
+                                       if (last_op-opline >= 3) { /* If we have more than 2 NOPS then JMP over them */
+                                               (opline+1)->opcode = ZEND_JMP;
+                                               ZEND_OP1(opline+1).opline_num = last_op - op_array->opcodes; /* that's OK even for ZE2, since opline_num's are resolved in pass 2 later */
+                                       }
+                               }
+                       }
+                       break;
+
+               case ZEND_FETCH_CONSTANT:
+                       if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
+                           ZEND_OP2_TYPE(opline) == IS_CONST &&
+                           Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
+                           Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__")-1 &&
+                           memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1) == 0) {
+                           /* substitute __COMPILER_HALT_OFFSET__ constant */
+                               zend_bool orig_in_execution = EG(in_execution);
+                               zend_op_array *orig_op_array = EG(active_op_array);
+                               zval offset;
+
+                               EG(in_execution) = 1;
+                               EG(active_op_array) = op_array;
+                               if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) {
+                                       literal_dtor(&ZEND_OP2_LITERAL(opline));
+                                       ZEND_OP1_TYPE(opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                       opline->op1.constant = zend_add_literal(op_array, &offset TSRMLS_CC);
+#else
+                                       ZEND_OP1_LITERAL(opline) = offset;
+#endif
+                                       SET_UNUSED(opline->op2);
+                                       opline->opcode = ZEND_QM_ASSIGN;
+                               }
+                               EG(active_op_array) = orig_op_array;
+                               EG(in_execution) = orig_in_execution;
+                               break;
+                       }
+
+                       if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
+                           ZEND_OP2_TYPE(opline) == IS_CONST &&
+                           ZEND_OP2_LITERAL(opline).type == IS_STRING) {
+                           /* substitute persistent constants */
+                               zval c;
+
+                               if(zend_get_persistent_constant(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), &c, 1 TSRMLS_CC) == 0) {
+                                       break;
+                               }
+                               literal_dtor(&ZEND_OP2_LITERAL(opline));
+                               ZEND_OP1_TYPE(opline) = IS_CONST;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               opline->op1.constant = zend_add_literal(op_array, &c TSRMLS_CC);
+#else
+                               ZEND_OP1_LITERAL(opline) = c;
+#endif
+                               SET_UNUSED(opline->op2);
+                               opline->opcode = ZEND_QM_ASSIGN;
+                       }
+                       break;
+
+               case ZEND_INIT_FCALL_BY_NAME:
+                       if(opline->extended_value == 0 /* not method */ &&
+                          ZEND_OP1_TYPE(opline) == IS_UNUSED &&
+                          ZEND_OP2_TYPE(opline) == IS_CONST) {
+                               if((opline+1)->opcode == ZEND_DO_FCALL_BY_NAME &&
+                                  (opline+1)->extended_value == 0) {
+                                       (opline+1)->opcode = ZEND_DO_FCALL;
+                                       COPY_NODE((opline+1)->op1, opline->op2);
+                                       zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1)));
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                       Z_HASH_P(&ZEND_OP1_LITERAL(opline+1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1))+1);
+                                       op_array->literals[(opline+1)->op1.constant].cache_slot = op_array->last_cache_slot++;
+#endif
+                                       MAKE_NOP(opline);
+                               }
+                       }
+                       break;
+               }
+               opline++;
+               i++;
+       }
+}
diff --git a/Optimizer/pass2.c b/Optimizer/pass2.c
new file mode 100644 (file)
index 0000000..ef6c5ac
--- /dev/null
@@ -0,0 +1,210 @@
+/* pass 2:
+ * - convert non-numeric constants to numeric constants in numeric operators
+ * - optimize constant conditional JMPs
+ * - optimize static BRKs and CONTs
+ */
+
+if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
+       zend_op *opline;
+       zend_op *end = op_array->opcodes+op_array->last;
+
+       opline = op_array->opcodes;
+       while (opline<end) {
+               switch (opline->opcode) {
+                       case ZEND_ADD:
+                       case ZEND_SUB:
+                       case ZEND_MUL:
+                       case ZEND_DIV:
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP1_LITERAL(opline).type == IS_STRING) {
+                                               convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC);
+                                       }
+                               }
+                               /* break missing *intentionally* - the assign_op's may only optimize op2 */
+                       case ZEND_ASSIGN_ADD:
+                       case ZEND_ASSIGN_SUB:
+                       case ZEND_ASSIGN_MUL:
+                       case ZEND_ASSIGN_DIV:
+                               if(opline->extended_value != 0) {
+                                       /* object tristate op - don't attempt to optimize it! */
+                                       break;
+                               }
+                               if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP2_LITERAL(opline).type == IS_STRING) {
+                                               convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC);
+                                       }
+                               }
+                               break;
+
+                       case ZEND_MOD:
+                       case ZEND_SL:
+                       case ZEND_SR:
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP1_LITERAL(opline).type != IS_LONG) {
+                                               convert_to_long(&ZEND_OP1_LITERAL(opline));
+                                       }
+                               }
+                               /* break missing *intentionally - the assign_op's may only optimize op2 */
+                       case ZEND_ASSIGN_MOD:
+                       case ZEND_ASSIGN_SL:
+                       case ZEND_ASSIGN_SR:
+                               if(opline->extended_value != 0) {
+                                       /* object tristate op - don't attempt to optimize it! */
+                                       break;
+                               }
+                               if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP2_LITERAL(opline).type != IS_LONG) {
+                                               convert_to_long(&ZEND_OP2_LITERAL(opline));
+                                       }
+                               }
+                               break;
+                       
+                       case ZEND_CONCAT:
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP1_LITERAL(opline).type != IS_STRING) {
+                                               convert_to_string(&ZEND_OP1_LITERAL(opline));
+                                       }
+                               }
+                               /* break missing *intentionally - the assign_op's may only optimize op2 */
+                       case ZEND_ASSIGN_CONCAT:
+                               if(opline->extended_value != 0) {
+                                       /* object tristate op - don't attempt to optimize it! */
+                                       break;
+                               }
+                               if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                                       if (ZEND_OP2_LITERAL(opline).type != IS_STRING) {
+                                               convert_to_string(&ZEND_OP2_LITERAL(opline));
+                                       }
+                               }
+                               break;
+                       
+                       case ZEND_JMPZ_EX:
+                       case ZEND_JMPNZ_EX:
+                               /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
+                               if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+                                   ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
+                                   ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
+                                       opline->opcode -= 3;
+                               /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
+                                  in case we know it wouldn't jump */
+                               } else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
+                                       if (opline->opcode == ZEND_JMPZ_EX) {
+                                               should_jmp = !should_jmp;
+                                       }
+                                       if (!should_jmp) {
+                                               opline->opcode = ZEND_QM_ASSIGN;
+                                               SET_UNUSED(opline->op2);
+                                       }
+                               }
+                               break;
+
+                       case ZEND_JMPZ:
+                       case ZEND_JMPNZ:
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
+
+                                       if (opline->opcode == ZEND_JMPZ) {
+                                               should_jmp = !should_jmp;
+                                       }
+                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                       ZEND_OP1_TYPE(opline) = IS_UNUSED;
+                                       if (should_jmp) {
+                                               opline->opcode = ZEND_JMP;
+                                               COPY_NODE(opline->op1, opline->op2);
+                                       } else {
+                                               MAKE_NOP(opline);
+                                       }
+                                       break;
+                               }
+                               if ((opline+1)->opcode == ZEND_JMP) {
+                                       /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
+                                       /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
+                                       if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline+1).opline_num) {
+                                               /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
+                                               MAKE_NOP(opline);
+                                       } else {
+                                               if (opline->opcode == ZEND_JMPZ) {
+                                                       opline->extended_value = ZEND_OP1(opline+1).opline_num;
+                                               } else {
+                                                       opline->extended_value = ZEND_OP2(opline).opline_num;
+                                                       COPY_NODE(opline->op2, (opline+1)->op1);
+                                               }
+                                               opline->opcode = ZEND_JMPZNZ;
+                                       }
+                               }
+                               break;
+
+                       case ZEND_JMPZNZ:
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                       int opline_num;
+
+                                       if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
+                                               opline_num = opline->extended_value; /* JMPNZ */
+                                       } else {
+                                               opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
+                                       }
+                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                       ZEND_OP1(opline).opline_num = opline_num;
+                                       ZEND_OP1_TYPE(opline) = IS_UNUSED;
+                                       opline->opcode = ZEND_JMP;
+                               }
+                               break;
+               
+                       case ZEND_BRK:
+                       case ZEND_CONT:
+                               {
+                                   zend_brk_cont_element *jmp_to;
+                                       int array_offset;
+                                       int nest_levels;
+                                       int dont_optimize=0;
+
+                                       if (ZEND_OP2_TYPE(opline) != IS_CONST) {
+                                               break;
+                                       }
+                                       convert_to_long(&ZEND_OP2_LITERAL(opline));
+                                       nest_levels = ZEND_OP2_LITERAL(opline).value.lval;
+
+                                       array_offset = ZEND_OP1(opline).opline_num;
+                                       while (1) {
+                                               if (array_offset==-1) {
+                                                       dont_optimize=1; /* don't optimize this bogus break/continue, let the executor shout */
+                                                       break;
+                                               }
+                                               jmp_to = &op_array->brk_cont_array[array_offset];
+                                               array_offset = jmp_to->parent;
+                                               if (--nest_levels > 0) {
+                                                       if (opline->opcode == ZEND_BRK &&
+                                                           (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE ||
+                                                            op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE)) {
+                                                               dont_optimize=1;
+                                                               break;
+                                                       }
+                                               } else {
+                                                       break;
+                                               }
+                                       }
+
+                                       if (dont_optimize) {
+                                               break;
+                                       }
+       
+                                       /* optimize - convert to a JMP */
+                                       switch (opline->opcode) {
+                                               case ZEND_BRK:
+                                                       MAKE_NOP(opline);
+                                                       ZEND_OP1(opline).opline_num = jmp_to->brk;
+                                                       break;
+                                               case ZEND_CONT:
+                                                       MAKE_NOP(opline);
+                                                       ZEND_OP1(opline).opline_num = jmp_to->cont;
+                                                       break;
+                                       }
+                                       opline->opcode = ZEND_JMP;
+                                       /* MAKE_NOP() already set op1 and op2 to IS_UNUSED */
+                               }
+                               break;
+               }
+               opline++;
+       }
+}
diff --git a/Optimizer/pass3.c b/Optimizer/pass3.c
new file mode 100644 (file)
index 0000000..95a6aad
--- /dev/null
@@ -0,0 +1,443 @@
+/* pass 3:
+ * - optimize $i = $i+expr to $i+=expr
+ * - optimize series of JMPs
+ * - change $i++ to ++$i where possible
+ */
+
+/* compares opcodes with allowing oc1 be _EX of oc2 */
+#define SAME_OPCODE_EX(oc1, oc2) ((oc1 == oc2) || (oc1 == ZEND_JMPZ_EX && oc2 == ZEND_JMPZ) || (oc1 == ZEND_JMPNZ_EX && oc2 == ZEND_JMPNZ))
+
+/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
+#define CHECK_JMP(target, label)                       \
+       for (i=0; i<jmp_hitlist_count; i++) {   \
+               if (jmp_hitlist[i] == ZEND_OP1(&op_array->opcodes[target]).opline_num) {                \
+                       goto label;                                             \
+               }                                                                       \
+       }                                                                               \
+       jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+
+#define CHECK_JMP2(target, label)                      \
+       for (i=0; i<jmp_hitlist_count; i++) {   \
+               if (jmp_hitlist[i] == ZEND_OP2(&op_array->opcodes[target]).opline_num) {                \
+                       goto label;                                             \
+               }                                                                       \
+       }                                                                               \
+       jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+
+if(ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) {
+       zend_op *opline;
+       zend_op *end = op_array->opcodes+op_array->last;
+       zend_uint *jmp_hitlist;
+       int jmp_hitlist_count;
+       int i;
+       zend_uint opline_num = 0;
+       ALLOCA_FLAG(use_heap);
+
+       jmp_hitlist = (zend_uint *) DO_ALLOCA(sizeof(zend_uint)*op_array->last);
+       opline = op_array->opcodes;
+
+       while (opline<end) {
+               jmp_hitlist_count = 0;
+
+               switch (opline->opcode) {
+                       case ZEND_ADD:
+                       case ZEND_SUB:
+                       case ZEND_MUL:
+                       case ZEND_DIV:
+                       case ZEND_MOD:
+                       case ZEND_CONCAT:
+                       case ZEND_SL:
+                       case ZEND_SR:
+                       case ZEND_BW_OR:
+                       case ZEND_BW_AND:
+                       case ZEND_BW_XOR:
+                               {
+                                       zend_op *next_opline = opline+1;
+
+                                       while (next_opline < end && next_opline->opcode == ZEND_NOP) {
+                                               ++next_opline;
+                                       }
+
+                                       if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) {
+                                               break;
+                                       }
+
+                                       if ((ZEND_OP2_TYPE(opline)==IS_VAR || ZEND_OP2_TYPE(opline)==IS_CV)
+                                               && ZEND_OP2(opline).var == ZEND_OP1(next_opline).var &&
+                                               (opline->opcode == ZEND_ADD ||
+                                                opline->opcode == ZEND_MUL ||
+                                                opline->opcode == ZEND_BW_OR ||
+                                                opline->opcode == ZEND_BW_AND ||
+                                                opline->opcode == ZEND_BW_XOR)) {
+                                               /* change $i=expr+$i to $i=$i+expr so that the next
+                                               * optimization works on it
+                                               */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                               zend_uchar tmp_type=opline->op1_type;
+                                               znode_op tmp=opline->op1;
+#else
+                                               znode tmp=opline->op1;
+#endif
+
+                                               if(opline->opcode != ZEND_ADD || ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                                       /* protection from array add: $a = array + $a is not commutative! */
+                                                       COPY_NODE(opline->op1, opline->op2);
+                                                       COPY_NODE(opline->op2, tmp);
+                                               }
+                                       }
+                                       if ((ZEND_OP1_TYPE(opline)==IS_VAR || ZEND_OP1_TYPE(opline)==IS_CV)
+                                               && ZEND_OP1(opline).var == ZEND_OP1(next_opline).var
+                                               && ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) {
+                                               switch (opline->opcode) {
+                                                       case ZEND_ADD:
+                                                               opline->opcode = ZEND_ASSIGN_ADD;
+                                                               break;
+                                                       case ZEND_SUB:
+                                                               opline->opcode = ZEND_ASSIGN_SUB;
+                                                               break;
+                                                       case ZEND_MUL:
+                                                               opline->opcode = ZEND_ASSIGN_MUL;
+                                                               break;
+                                                       case ZEND_DIV:
+                                                               opline->opcode = ZEND_ASSIGN_DIV;
+                                                               break;
+                                                       case ZEND_MOD:
+                                                               opline->opcode = ZEND_ASSIGN_MOD;
+                                                               break;
+                                                       case ZEND_CONCAT:
+                                                               opline->opcode = ZEND_ASSIGN_CONCAT;
+                                                               break;
+                                                       case ZEND_SL:
+                                                               opline->opcode = ZEND_ASSIGN_SL;
+                                                               break;
+                                                       case ZEND_SR:
+                                                               opline->opcode = ZEND_ASSIGN_SR;
+                                                               break;
+                                                       case ZEND_BW_OR:
+                                                               opline->opcode = ZEND_ASSIGN_BW_OR;
+                                                               break;
+                                                       case ZEND_BW_AND:
+                                                               opline->opcode = ZEND_ASSIGN_BW_AND;
+                                                               break;
+                                                       case ZEND_BW_XOR:
+                                                               opline->opcode = ZEND_ASSIGN_BW_XOR;
+                                                               break;
+                                               }
+                                               COPY_NODE(opline->result, next_opline->result);
+                                               MAKE_NOP(next_opline);
+                                               opline++;
+                                               opline_num++;
+                                       }
+                               }
+                               break;
+
+                       case ZEND_JMP:
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               if (op_array->has_finally_block) {
+                                       break;
+                               }
+#endif
+
+                               /* convert L: JMP L+1 to NOP */
+                               if(ZEND_OP1(opline).opline_num == opline_num + 1) {
+                                       MAKE_NOP(opline);
+                                       goto done_jmp_optimization;
+                               }
+
+                               /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
+                               while (ZEND_OP1(opline).opline_num<op_array->last
+                                               && op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) {
+                                       int target = ZEND_OP1(opline).opline_num;
+                                       CHECK_JMP(target, done_jmp_optimization);
+                                       ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+                               }
+                               break;
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                       case ZEND_JMP_SET:
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       case ZEND_JMP_SET_VAR:
+#endif
+
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               if (op_array->has_finally_block) {
+                                       break;
+                               }
+#endif
+
+                               while (ZEND_OP2(opline).opline_num<op_array->last) {
+                                       int target = ZEND_OP2(opline).opline_num;
+                                       if (op_array->opcodes[target].opcode == ZEND_JMP) {
+                                               ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               break;
+#endif
+
+                       case ZEND_JMPZ:
+                       case ZEND_JMPNZ:
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               if (op_array->has_finally_block) {
+                                       break;
+                               }
+#endif
+
+                               /* convert L: JMPZ L+1 to NOP */
+                               if(ZEND_OP2(opline).opline_num == opline_num + 1) {
+                                       MAKE_NOP(opline);
+                                       goto done_jmp_optimization;
+                               }
+
+                               while (ZEND_OP2(opline).opline_num<op_array->last) {
+                                       int target = ZEND_OP2(opline).opline_num;
+
+                                       if (op_array->opcodes[target].opcode == ZEND_JMP) {
+                                               /* plain JMP */
+                                               /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
+                                               CHECK_JMP(target,  done_jmp_optimization);
+                                               ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+                                       } else if (op_array->opcodes[target].opcode == opline->opcode &&
+                                                  SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+                                               /* same opcode and same var as this opcode */
+                                               /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
+                                               CHECK_JMP2(target,  done_jmp_optimization);
+                                               ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+                                       } else if (op_array->opcodes[target].opcode == opline->opcode+3 &&
+                                                  SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+                                               /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to
+                                                  T = JMPZ_EX(X, L2) */
+                                               ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3;
+                                               COPY_NODE(opline->result, op_array->opcodes[target].result);
+                                               break;
+                                       } else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) &&
+                                                  SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+                                               /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
+                                                  JMPZ(X,L1+1) */
+                                               ZEND_OP2(opline).opline_num = target+1;
+                                               break;
+                                       } else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) &&
+                                                  SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+                                               /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to
+                                                  T = JMPZ_EX(X,L1+1) */
+                                               ZEND_OP2(opline).opline_num = target+1;
+                                               opline->opcode += 3;
+                                               COPY_NODE(opline->result, op_array->opcodes[target].result);
+                                               break;
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               break;
+
+                       case ZEND_JMPZ_EX:
+                       case ZEND_JMPNZ_EX: {
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                       zend_uchar T_type = opline->result_type;
+                                       znode_op T = opline->result;
+#else
+                                       znode T = opline->result;
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                                       if (op_array->has_finally_block) {
+                                               break;
+                                       }
+#endif
+                                       /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
+                                       /* convert L: T = JMPZ_EX T,L+1 to NOP */
+                                       if(ZEND_OP2(opline).opline_num == opline_num + 1) {
+                                               if(ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
+                                                       MAKE_NOP(opline);
+                                               } else {
+                                                       opline->opcode = ZEND_BOOL;
+                                                       SET_UNUSED(opline->op2);
+                                               }
+                                               goto done_jmp_optimization;
+                                       }
+
+                                       while (ZEND_OP2(opline).opline_num<op_array->last) {
+                                               int target = ZEND_OP2(opline).opline_num;
+                                               if(SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) &&
+                                                  SAME_VAR(op_array->opcodes[target].op1, T)) {
+                                                       /* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */
+                                                       if(op_array->opcodes[target].opcode == opline->opcode) {
+                                                               /* change T only if we have _EX opcode there */
+                                                               COPY_NODE(T, op_array->opcodes[target].result);
+                                                       }
+                                                       CHECK_JMP2(target, continue_jmp_ex_optimization);
+                                                       ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+                                               } else if(op_array->opcodes[target].opcode == ZEND_JMPZNZ &&
+                                                                 SAME_VAR(op_array->opcodes[target].op1, T)) {
+                                                       /* Check for JMPZNZ with same cond variable */
+                                                       int new_target;
+                                                       CHECK_JMP2(target, continue_jmp_ex_optimization);
+                                                       if(opline->opcode == ZEND_JMPZ_EX) {
+                                                               new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+                                                       } else {
+                                                               /* JMPNZ_EX */
+                                                               new_target = op_array->opcodes[target].extended_value;
+                                                       }
+                                                       ZEND_OP2(opline).opline_num = new_target;
+                                               } else if((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) ||
+                                                                  op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode)
+                                                                  ) &&
+                                                                SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+                                                  /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to
+                                                         JMPZ_EX(X,L1+1) */
+                                                  ZEND_OP2(opline).opline_num = target+1;
+                                                  break;
+                                               } else {
+                                                       break;
+                                               }
+                                       } /* while */
+continue_jmp_ex_optimization:
+                                       break;
+#if 0
+                                       /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */
+                                       {
+                                               zend_op *op;
+                                               for(op = opline+1; op<end; op++) {
+                                                       if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_RESULT(op).var == ZEND_RESULT(opline).var) {
+                                                               break; /* can pass to part 2 */
+                                                       }
+
+                                                       if(op->opcode == ZEND_JMP ||
+                                                          op->opcode == ZEND_JMPZ ||
+                                                          op->opcode == ZEND_JMPZ_EX ||
+                                                          op->opcode == ZEND_JMPNZ ||
+                                                          op->opcode == ZEND_JMPNZ_EX ||
+                                                          op->opcode == ZEND_JMPZNZ ||
+                                                          op->opcode == ZEND_BRK ||
+                                                          op->opcode == ZEND_CONT ||
+                                                          op->opcode == ZEND_CASE ||
+                                                          op->opcode == ZEND_RETURN ||
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                                          op->opcode == ZEND_RETURN_BY_REF ||
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                                                          op->opcode == ZEND_FAST_RET ||
+#endif
+                                                          op->opcode == ZEND_FE_FETCH ||
+                                                          op->opcode == ZEND_EXIT) {
+                                                               break;
+                                                       }
+
+                                                       if(ZEND_OP1_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_OP1(op).var == ZEND_RESULT(opline).var) {
+                                                               goto done_jmp_optimization;
+                                                       }
+
+                                                       if(ZEND_OP2_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_OP2(op).var == ZEND_RESULT(opline).var) {
+                                                               goto done_jmp_optimization;
+                                                       }
+                                               } /* for */
+
+                                               for(op = &op_array->opcodes[ZEND_OP2(opline).opline_num]; op<end; op++) {
+
+                                                       if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_RESULT(op).var == ZEND_RESULT(opline).var) {
+                                                               break; /* can pass to optimization */
+                                                       }
+
+                                                       if(op->opcode == ZEND_JMP ||
+                                                          op->opcode == ZEND_JMPZ ||
+                                                          op->opcode == ZEND_JMPZ_EX ||
+                                                          op->opcode == ZEND_JMPNZ ||
+                                                          op->opcode == ZEND_JMPNZ_EX ||
+                                                          op->opcode == ZEND_JMPZNZ ||
+                                                          op->opcode == ZEND_BRK ||
+                                                          op->opcode == ZEND_CONT ||
+                                                          op->opcode == ZEND_CASE ||
+                                                          op->opcode == ZEND_RETURN ||
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                                          op->opcode == ZEND_RETURN_BY_REF ||
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                                                          op->opcode == ZEND_FAST_RET ||
+#endif
+                                                          op->opcode == ZEND_FE_FETCH ||
+                                                          op->opcode == ZEND_EXIT) {
+                                                               break;
+                                                       }
+
+                                                       if(ZEND_OP1_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_OP1(op).var == ZEND_RESULT(opline).var) {
+                                                               goto done_jmp_optimization;
+                                                       }
+
+                                                       if(ZEND_OP2_TYPE(op) == IS_TMP_VAR &&
+                                                          ZEND_OP2(op).var == ZEND_RESULT(opline).var) {
+                                                               goto done_jmp_optimization;
+                                                       }
+                                               }
+
+                                               opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */
+                                               SET_UNUSED(opline->result);
+                                               break;
+                                       }
+#endif
+                               }
+                               break;
+
+                       case ZEND_JMPZNZ:
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                               if (op_array->has_finally_block) {
+                                       break;
+                               }
+#endif
+                               /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
+                               while (ZEND_OP2(opline).opline_num < op_array->last
+                                               && op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) {
+                                       int target = ZEND_OP2(opline).opline_num;
+                                       CHECK_JMP(target, continue_jmpznz_optimization);
+                                       ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+                               }
+continue_jmpznz_optimization:
+                               /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
+                               while (opline->extended_value < op_array->last
+                                               && op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) {
+                                       int target = opline->extended_value;
+                                       CHECK_JMP(target, done_jmp_optimization);
+                                       opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+                               }
+                               break;
+
+                       case ZEND_POST_INC:
+                       case ZEND_POST_DEC: {
+                                       /* POST_INC, FREE => PRE_INC */
+                                       zend_op *next_op = opline+1;
+
+                                       if (next_op>=end) {
+                                               break;
+                                       }
+                                       if (next_op->opcode == ZEND_FREE
+                                               && ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) {
+                                               MAKE_NOP(next_op);
+                                               switch (opline->opcode) {
+                                                       case ZEND_POST_INC:
+                                                               opline->opcode = ZEND_PRE_INC;
+                                                               break;
+                                                       case ZEND_POST_DEC:
+                                                               opline->opcode = ZEND_PRE_DEC;
+                                                               break;
+                                               }
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                               ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED;
+#else
+                                               ZEND_RESULT_TYPE(opline) = IS_VAR;
+                                               ZEND_RESULT(opline).EA.type = 0;
+                                               ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED;
+#endif
+                                       }
+                               }
+                               break;
+               }
+done_jmp_optimization:
+               opline++;
+               opline_num++;
+       }
+       FREE_ALLOCA(jmp_hitlist);
+}
diff --git a/Optimizer/pass5.c b/Optimizer/pass5.c
new file mode 100644 (file)
index 0000000..b0d651a
--- /dev/null
@@ -0,0 +1,3 @@
+if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) {
+       zend_block_optimization(op_array TSRMLS_CC);
+}
diff --git a/Optimizer/pass9.c b/Optimizer/pass9.c
new file mode 100644 (file)
index 0000000..586160c
--- /dev/null
@@ -0,0 +1,8 @@
+/* pass 9
+ *
+ * - optimize usage of temporary variables
+ */
+
+if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) {
+       optimize_temporary_variables(op_array);
+}
diff --git a/Optimizer/zend_optimizer.c b/Optimizer/zend_optimizer.c
new file mode 100644 (file)
index 0000000..b2b0c88
--- /dev/null
@@ -0,0 +1,139 @@
+/*\r
+   +----------------------------------------------------------------------+\r
+   | Zend Optimizer+                                                      |\r
+   +----------------------------------------------------------------------+\r
+   | Copyright (c) 1998-2013 The PHP Group                                |\r
+   +----------------------------------------------------------------------+\r
+   | This source file is subject to version 3.01 of the PHP license,      |\r
+   | that is bundled with this package in the file LICENSE, and is        |\r
+   | available through the world-wide-web at the following url:           |\r
+   | http://www.php.net/license/3_01.txt                                  |\r
+   | If you did not receive a copy of the PHP license and are unable to   |\r
+   | obtain it through the world-wide-web, please send a note to          |\r
+   | license@php.net so we can mail you a copy immediately.               |\r
+   +----------------------------------------------------------------------+\r
+   | Authors: Andi Gutmans <andi@zend.com>                                |\r
+   |          Zeev Suraski <zeev@zend.com>                                |\r
+   |          Stanislav Malyshev <stas@zend.com>                          |\r
+   |          Dmitry Stogov <dmitry@zend.com>                             |\r
+   +----------------------------------------------------------------------+\r
+*/\r
+\r
+#include "Optimizer/zend_optimizer.h"\r
+#include "Optimizer/zend_optimizer_internal.h"\r
+#include "zend_API.h"\r
+#include "zend_constants.h"\r
+#include "zend_execute.h"\r
+\r
+#define OPTIMIZATION_LEVEL \\r
+       ZCG(accel_directives).optimization_level\r
+\r
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO\r
+int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC)\r
+{\r
+       int i = op_array->last_literal;\r
+       op_array->last_literal++;\r
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO\r
+       {\r
+               if (i >= CG(context).literals_size) {\r
+                       CG(context).literals_size += 16; /* FIXME */\r
+                       op_array->literals = (zend_literal*)erealloc(op_array->literals, CG(context).literals_size * sizeof(zend_literal));\r
+               }\r
+       }\r
+#else\r
+       if (i >= op_array->size_literal) {\r
+               op_array->size_literal += 16; /* FIXME */\r
+               op_array->literals = (zend_literal*)erealloc(op_array->literals, op_array->size_literal * sizeof(zend_literal));\r
+       }\r
+#endif\r
+       op_array->literals[i].constant = *zv;\r
+       Z_SET_REFCOUNT(op_array->literals[i].constant, 2);\r
+       Z_SET_ISREF(op_array->literals[i].constant);\r
+       return i;\r
+}\r
+\r
+# define LITERAL_LONG(op, val) do { \\r
+               zval _c; \\r
+               ZVAL_LONG(&_c, val); \\r
+               op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \\r
+       } while (0)\r
+\r
+# define LITERAL_BOOL(op, val) do { \\r
+               zval _c; \\r
+               ZVAL_BOOL(&_c, val); \\r
+               op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \\r
+       } while (0)\r
+\r
+# define literal_dtor(zv) do { \\r
+               zval_dtor(zv); \\r
+               Z_TYPE_P(zv) = IS_NULL; \\r
+       } while (0)\r
+\r
+#define COPY_NODE(target, src) do { \\r
+               target ## _type = src ## _type; \\r
+               target = src; \\r
+       } while (0)\r
+\r
+#else\r
+\r
+# define LITERAL_LONG(op, val) ZVAL_LONG(&op.u.constant, val)\r
+\r
+# define LITERAL_BOOL(op, val) ZVAL_BOOL(&op.u.constant, val)\r
+\r
+# define literal_dtor(zv) zval_dtor(zv)\r
+\r
+#define COPY_NODE(target, src) do { \\r
+               target = src; \\r
+       } while (0)\r
+\r
+#endif\r
+\r
+#include "Optimizer/nop_removal.c"\r
+#include "Optimizer/block_pass.c"\r
+#include "Optimizer/optimize_temp_vars_5.c"\r
+\r
+void zend_optimizer(zend_op_array *op_array TSRMLS_DC)\r
+{\r
+       if (op_array->type == ZEND_EVAL_CODE ||\r
+           (op_array->fn_flags & ZEND_ACC_INTERACTIVE)) {\r
+               return;\r
+       }\r
+\r
+       /* pass 1\r
+        * - substitute persistent constants (true, false, null, etc)\r
+        * - perform compile-time evaluation of constant binary and unary operations\r
+        * - optimize series of ADD_STRING and/or ADD_CHAR\r
+        * - convert CAST(IS_BOOL,x) into BOOL(x)\r
+        * - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL\r
+        */\r
+#include "Optimizer/pass1_5.c"\r
+\r
+       /* pass 2:\r
+        * - convert non-numeric constants to numeric constants in numeric operators\r
+        * - optimize constant conditional JMPs\r
+        * - optimize static BRKs and CONTs\r
+        */\r
+#include "Optimizer/pass2.c"\r
+\r
+       /* pass 3:\r
+        * - optimize $i = $i+expr to $i+=expr\r
+        * - optimize series of JMPs\r
+        * - change $i++ to ++$i where possible\r
+        */\r
+#include "Optimizer/pass3.c"\r
+\r
+       /* pass 5:\r
+        * - CFG optimization\r
+        */\r
+#include "Optimizer/pass5.c"\r
+\r
+        /* pass 9:\r
+        * - Optimize temp variables usage\r
+        */\r
+#include "Optimizer/pass9.c"\r
+\r
+       /* pass 10:\r
+        * - remove NOPs\r
+        */\r
+#include "Optimizer/pass10.c"\r
+}\r
diff --git a/Optimizer/zend_optimizer.h b/Optimizer/zend_optimizer.h
new file mode 100644 (file)
index 0000000..0c06916
--- /dev/null
@@ -0,0 +1,49 @@
+/*\r
+   +----------------------------------------------------------------------+\r
+   | Zend Optimizer+                                                      |\r
+   +----------------------------------------------------------------------+\r
+   | Copyright (c) 1998-2013 The PHP Group                                |\r
+   +----------------------------------------------------------------------+\r
+   | This source file is subject to version 3.01 of the PHP license,      |\r
+   | that is bundled with this package in the file LICENSE, and is        |\r
+   | available through the world-wide-web at the following url:           |\r
+   | http://www.php.net/license/3_01.txt                                  |\r
+   | If you did not receive a copy of the PHP license and are unable to   |\r
+   | obtain it through the world-wide-web, please send a note to          |\r
+   | license@php.net so we can mail you a copy immediately.               |\r
+   +----------------------------------------------------------------------+\r
+   | Authors: Andi Gutmans <andi@zend.com>                                |\r
+   |          Zeev Suraski <zeev@zend.com>                                |\r
+   |          Stanislav Malyshev <stas@zend.com>                          |\r
+   |          Dmitry Stogov <dmitry@zend.com>                             |\r
+   +----------------------------------------------------------------------+\r
+*/\r
+\r
+#ifndef ZEND_OPTIMIZER_H\r
+#define ZEND_OPTIMIZER_H\r
+\r
+#include "zend.h"\r
+#include "zend_compile.h"\r
+\r
+#define ZEND_OPTIMIZER_PASS_1          (1<<0)   /* CSE, STRING construction     */\r
+#define ZEND_OPTIMIZER_PASS_2          (1<<1)   /* Constant conversion and jums */\r
+#define ZEND_OPTIMIZER_PASS_3          (1<<2)   /* ++, +=, series of jumps      */\r
+#define ZEND_OPTIMIZER_PASS_4          (1<<3)\r
+#define ZEND_OPTIMIZER_PASS_5          (1<<4)   /* CFG based optimization       */\r
+#define ZEND_OPTIMIZER_PASS_6          (1<<5)\r
+#define ZEND_OPTIMIZER_PASS_7          (1<<6)\r
+#define ZEND_OPTIMIZER_PASS_8          (1<<7)   \r
+#define ZEND_OPTIMIZER_PASS_9          (1<<8)   /* TMP VAR usage                */\r
+#define ZEND_OPTIMIZER_PASS_10         (1<<9)   /* NOP removal                 */\r
+#define ZEND_OPTIMIZER_PASS_11         (1<<10)\r
+#define ZEND_OPTIMIZER_PASS_12         (1<<11)\r
+#define ZEND_OPTIMIZER_PASS_13         (1<<12)\r
+#define ZEND_OPTIMIZER_PASS_14         (1<<13)\r
+\r
+#define ZEND_OPTIMIZER_ALL_PASSES      0xFFFFFFFF\r
+\r
+#define DEFAULT_OPTIMIZATION_LEVEL  "0xFFFFFFFF"\r
+\r
+void zend_optimizer(zend_op_array *op_array TSRMLS_DC);\r
+\r
+#endif\r
diff --git a/Optimizer/zend_optimizer_internal.h b/Optimizer/zend_optimizer_internal.h
new file mode 100644 (file)
index 0000000..113cbe9
--- /dev/null
@@ -0,0 +1,76 @@
+/*\r
+   +----------------------------------------------------------------------+\r
+   | Zend Optimizer+                                                      |\r
+   +----------------------------------------------------------------------+\r
+   | Copyright (c) 1998-2013 The PHP Group                                |\r
+   +----------------------------------------------------------------------+\r
+   | This source file is subject to version 3.01 of the PHP license,      |\r
+   | that is bundled with this package in the file LICENSE, and is        |\r
+   | available through the world-wide-web at the following url:           |\r
+   | http://www.php.net/license/3_01.txt                                  |\r
+   | If you did not receive a copy of the PHP license and are unable to   |\r
+   | obtain it through the world-wide-web, please send a note to          |\r
+   | license@php.net so we can mail you a copy immediately.               |\r
+   +----------------------------------------------------------------------+\r
+   | Authors: Andi Gutmans <andi@zend.com>                                |\r
+   |          Zeev Suraski <zeev@zend.com>                                |\r
+   |          Stanislav Malyshev <stas@zend.com>                          |\r
+   |          Dmitry Stogov <dmitry@zend.com>                             |\r
+   +----------------------------------------------------------------------+\r
+*/\r
+\r
+#ifndef ZEND_OPTIMIZER_INTERNAL_H\r
+#define ZEND_OPTIMIZER_INTERNAL_H\r
+\r
+#include "ZendAccelerator.h"\r
+\r
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO\r
+# define VAR_NUM(v) (EX_TMP_VAR_NUM(0, 0) - EX_TMP_VAR(0, v))\r
+# define NUM_VAR(v) ((zend_uint)EX_TMP_VAR_NUM(0, v))\r
+#else\r
+# define VAR_NUM(v) ((v)/(sizeof(temp_variable)))\r
+# define NUM_VAR(v) ((v)*(sizeof(temp_variable)))\r
+#endif\r
+\r
+#define INV_COND(op)       ((op) == ZEND_JMPZ    ? ZEND_JMPNZ    : ZEND_JMPZ)\r
+#define INV_EX_COND(op)    ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ    : ZEND_JMPZ)\r
+#define INV_COND_EX(op)    ((op) == ZEND_JMPZ    ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)\r
+#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)\r
+\r
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO\r
+# define MAKE_NOP(opline)      { opline->opcode = ZEND_NOP;  memset(&opline->result,0,sizeof(opline->result)); memset(&opline->op1,0,sizeof(opline->op1)); memset(&opline->op2,0,sizeof(opline->op2)); opline->result_type=opline->op1_type=opline->op2_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }\r
+# define RESULT_USED(op)       (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR)\r
+# define RESULT_UNUSED(op)     ((op->result_type & EXT_TYPE_UNUSED) != 0)\r
+# define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var)\r
+#else\r
+# define MAKE_NOP(opline)      { opline->opcode = ZEND_NOP;  memset(&opline->result,0,sizeof(znode)); memset(&opline->op1,0,sizeof(znode)); memset(&opline->op2,0,sizeof(znode)); opline->result.op_type=opline->op1.op_type=opline->op2.op_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }\r
+# define RESULT_USED(op)       ((op->result.op_type == IS_VAR && (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || (op->result.op_type == IS_TMP_VAR))\r
+# define RESULT_UNUSED(op)     ((op->result.op_type == IS_VAR) && (op->result.u.EA.type == EXT_TYPE_UNUSED))\r
+# define SAME_VAR(op1, op2)    (((op1.op_type == IS_VAR && op2.op_type == IS_VAR) || (op1.op_type == IS_TMP_VAR && op2.op_type == IS_TMP_VAR)) && op1.u.var == op2.u.var)\r
+#endif\r
+\r
+typedef struct _zend_code_block zend_code_block;\r
+typedef struct _zend_block_source zend_block_source;\r
+\r
+struct _zend_code_block {\r
+       int                 access;\r
+       zend_op            *start_opline;\r
+       int                 start_opline_no;\r
+       int                 len;\r
+       zend_code_block    *op1_to;\r
+       zend_code_block    *op2_to;\r
+       zend_code_block    *ext_to;\r
+       zend_code_block    *follow_to;\r
+       zend_code_block    *next;\r
+       zend_block_source  *sources;\r
+       zend_code_block   **try;\r
+       zend_code_block   **catch;\r
+       zend_bool           is_try;\r
+};\r
+\r
+struct _zend_block_source {\r
+       zend_code_block    *from;\r
+       zend_block_source  *next;\r
+};\r
+\r
+#endif\r
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..69273d0
--- /dev/null
+++ b/README
@@ -0,0 +1,171 @@
+The Zend Optimizer+
+===================
+
+The Zend Optimizer+ provides faster PHP execution through opcode caching and
+optimization. It improves PHP performance by storing precompiled script
+bytecode in the shared memory. This eliminates the stages of reading code from
+the disk and compiling it on future access. In addition it applies a few
+bytecode optimization patterns that make code execution faster.
+
+Compatibility
+-------------
+
+This version of Zend Optimizer+ is compatible with PHP 5.2.*, 5.3.*, 5.4.*
+and PHP-5.5 development branch.  PHP 5.2 support may be removed in the future.
+
+Quick Install
+-------------
+
+- Compile
+
+export PHP_DIR=/usr/local/php5.5
+PHP_AUTOCONF=autoconf $PHP_DIR/bin/phpize
+./configure \
+      --enable-optimizer-plus \
+      --with-php-config=$PHP_DIR/bin/php-config
+make
+
+- Install
+
+cp .libs/ZendOptimizerPlus.so $PHP_DIR/lib/ZendOptimizerPlus.so
+
+- Edit php.ini
+
+zend_extensin=/...full path.../ZendOptimizerPlus.so
+
+- Restart PHP
+
+Speed Tunning
+-------------
+
+We reccomend the following configuration options for best performance.
+
+zend_optimizerplus.memory_consumption=128
+zend_optimizerplus.interned_strings_buffer=8
+zend_optimizerplus.max_accelerated_files=4000
+zend_optimizerplus.revalidate_freq=60
+zend_optimizerplus.save_comments=0
+zend_optimizerplus.fast_shutdown=1
+zend_optimizerplus.enable_file_override=1
+zend_optimizerplus.enable_cli=1
+
+In some cases you may like to prefer enabling/disabling some features
+to avoid incompatibilities at the cost of some performance degradation.
+
+Configuration Directives
+------------------------
+
+zend_optimizerplus.enable (default "1")
+       Optimizer+ On/Off switch. When set to Off, code is not optimized.
+
+zend_optimizerplus.memory_consumption (default "64")
+       The Optimizer+ shared memory storage size. The amount of memory for storing
+       precompiled PHP code in Mbytes.
+
+zend_optimizerplus.interned_strings_buffer (default "4")
+       The amount of memory for interned strings in Mbytes.
+
+zend_optimizerplus.max_accelerated_files (default "2000")
+       The maximum number of keys (scripts) in the Optimizer+ hash table.
+       The number is actually the the first one in the following set of prime
+       numbers that is bigger than the one supplied: { 223, 463, 983, 1979, 3907,
+       7963, 16229, 32531, 65407, 130987 }. Only numbers between 200 and 100000
+       are allowed.
+
+zend_optimizerplus.max_wasted_percentage (default "5")
+       The maximum percentage of "wasted" memory until a restart is scheduled
+
+zend_optimizerplus.use_cwd (default "1")
+       When this directive is enabled, the Optimizer+ appends the current working
+       directory to the script key, thus elminating possible collisions between
+       files with the same name (basename). Disablingthe directive improves
+       performance, but may break existing applications.
+
+zend_optimizerplus.validate_timestamps (default "1")
+       When disabled, you must reset the Optimizer+ manually or restart the
+       webserver for changes to the filesystem to take effect.
+       The frequancy of the check is controlled by the directive
+       "zend_optimizerplus.revalidate_freq"
+
+zend_optimizerplus.revalidate_freq (default "2")
+       How often (in seconds) to check file timestamps for changes to the shared
+       memory storage allocation.
+
+zend_optimizerplus.revalidate_path (default "0")
+       Enables or disables file search in include_path optimization
+       If the file search is disabled and a cached file is found that uses
+       the same include_path, the file is not searched again. Thus, if a file
+       with the same name appears somewhere else in include_path, it
+       won't be found. Enable this directive if this optimization has an effect on
+       your applications. The default for this directive is disabled, which means
+       that optimization is active.
+
+zend_optimizerplus.save_comments (default "1")
+       If disabled, all PHPDoc comments are dropped from the code to reduce the
+       size of the optimized code.
+
+zend_optimizerplus.fast_shutdown (default "0")
+       If enabled, a fast shutdown sequence is used for the accelerated code
+       The fast shutdown sequence doesn't free each allocated block, but lets
+       the Zend Engine Memory Manager do the work.
+
+zend_optimizerplus.enable_file_override (default "0")
+       Allow file existance override (file_exists, etc.) performance feature
+
+zend_optimizerplus.optimization_level (default "0xffffffff")
+       A bitmask, where each bit enables or disables the appropriate Optimizer+
+       passes
+
+zend_optimizerplus.inherited_hack (default "1")
+       Enable this hack as a workaround for "can't redeclare class" errors.
+       The Optimizer+ stores the places where DECLARE_CLASS opcodes use
+       inheritance (These are the only opcodes that can be executed by PHP,
+       but which may not be executed because the parent class is missing due to
+       optimization). When the file is loaded, Optimizer+ tries to bind the
+       inherited classes by using the current environment. The problem with this
+       scenario is that, while the DECLARE_CLASS opcode may not be needed for the
+       current script, if the script requires that the opcode at least be defined,
+       it may not run. The default for this directive is disabled, which means
+       that optimization is active. In php-5.3 and above this hack is not needed
+       anymore and this setting has no effect.
+
+zend_optimizerplus.dups_fix (default "0")
+       Enable this hack as a workaround for "duplicate definition" errors
+
+zend_optimizerplus.blacklist_filename
+       The location of the Optimizer+ blacklist file
+       The Optimizer+ blacklist file is a text file that holds the names of files
+       that should not be accelerated. The file format is to add each filename
+       to a new line. The filename may be a full path or just a file prefix
+       (i.e., /var/www/x  blacklists all the files and directories in /var/www
+       that start with 'x'). Files are usually triggered by one of the following
+       three reasons:
+       1) Directories that contain auto generated code, like Smarty or ZFW cache.
+       2) Code that does not work well when accelerated, due to some delayed
+          compile time evaluation.
+       3) Code that triggers an Optimizer+ bug.
+
+zend_optimizerplus.consistency_checks (default "0")
+       Check the cache checksum each N requests.
+       The default value of "0" means that the checks are disabled.
+       Because calculating the checksum impairs performance, this directive should
+       be enabled only as part of a debugging process.
+
+zend_optimizerplus.force_restart_timeout (default "180")
+       How long to wait (in seconds) for a scheduled restart to begin if the cache
+       is not being accessed.
+       The Optimizer+ uses this directive to identify a situation where there may
+       be a problem with a process. After this time period has passed, the
+       Optimizer+ assumes that something has happened and starts killing the
+       processes that still hold the locks that are preventing a restart.
+       If the log level is 3 or above, a "killed locker" error is recorded
+       in the Apache logs when this happens.
+
+zend_optimizerplus.error_log
+       Optimizer+ error_log file name. Empty string assumes "stderr"
+
+zend_optimizerplus.log_verbosity_level (default "1")
+       Alll Optimizer+ errors go to the Web server log.
+       By default, only fatal errors (level 0) or errors (level 1) are logged.
+       You can also enable warnings (level 2), info messages (level 3) or
+       debug messesges (level 4).
diff --git a/ZendAccelerator.c b/ZendAccelerator.c
new file mode 100644 (file)
index 0000000..8b2a6ae
--- /dev/null
@@ -0,0 +1,2510 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "main/php.h"
+#include "main/php_globals.h"
+#include "zend.h"
+#include "zend_extensions.h"
+#include "zend_compile.h"
+#include "ZendAccelerator.h"
+#include "zend_persist.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_module.h"
+#include "zend_accelerator_blacklist.h"
+#include "zend_list.h"
+#include "zend_execute.h"
+#include "main/SAPI.h"
+#include "main/php_streams.h"
+#include "main/php_open_temporary_file.h"
+#include "zend_API.h"
+#include "zend_ini.h"
+#include "TSRM/tsrm_virtual_cwd.h"
+#include "zend_accelerator_util_funcs.h"
+#include "zend_accelerator_hash.h"
+
+#ifndef ZEND_WIN32
+#include  <netdb.h>
+#endif
+
+#ifdef ZEND_WIN32
+typedef int uid_t;
+typedef int gid_t;
+#include <io.h>
+#endif
+
+#ifndef ZEND_WIN32
+# include <sys/time.h>
+#else
+# include <process.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+
+#ifndef ZEND_WIN32
+# include <sys/types.h>
+# include <sys/ipc.h>
+#endif
+
+#include <sys/stat.h>
+#include <errno.h>
+
+#define MIN_FREE_MEMORY 64*1024
+
+#define SHM_PROTECT() \
+       do { \
+               if (ZCG(accel_directives).protect_memory) { \
+                       zend_accel_shared_protect(1 TSRMLS_CC); \
+               } \
+       } while (0)
+#define SHM_UNPROTECT() \
+       do { \
+               if (ZCG(accel_directives).protect_memory) { \
+                       zend_accel_shared_protect(0 TSRMLS_CC); \
+               } \
+       } while (0)
+
+ZEND_EXTENSION();
+
+#ifndef ZTS
+zend_accel_globals accel_globals;
+#else
+int accel_globals_id;
+#endif
+
+/* Points to the structure shared across all PHP processes */
+zend_accel_shared_globals *accel_shared_globals = NULL;
+
+/* true globals, no need for thread safety */
+static char *zps_failure_reason = NULL;
+char *zps_api_failure_reason = NULL;
+
+static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
+static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle  TSRMLS_DC);
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+static char *(*accelerator_orig_zend_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
+#endif
+static void (*orig_chdir)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
+static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
+
+#ifdef ZEND_WIN32
+# define INCREMENT(v) InterlockedIncrement(&ZCSG(v))
+# define DECREMENT(v) InterlockedDecrement(&ZCSG(v))
+# define LOCKVAL(v)   (ZCSG(v))
+#endif
+
+#ifdef ZEND_WIN32
+static time_t zend_accel_get_time(void)
+{
+       FILETIME now;
+       GetSystemTimeAsFileTime(&now);
+
+       return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
+}
+#else
+# define zend_accel_get_time() time(NULL)
+#endif
+
+static inline int is_stream_path(const char *filename)
+{
+       const char *p;
+
+       for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
+       return ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/'));
+}
+
+/* O+ overrides PHP chdir() function and remembers the current working directory
+ * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
+ * avoid getcwd() call.
+ */
+static ZEND_FUNCTION(accel_chdir)
+{
+       char cwd[MAXPATHLEN];
+
+       orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+    if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
+       ZCG(cwd_len) = strlen(cwd);
+       ZCG(cwd) = estrndup(cwd, ZCG(cwd_len));
+    } else {
+       if (ZCG(cwd)) {
+               efree(ZCG(cwd));
+               ZCG(cwd) = NULL;
+       }
+    }
+}
+
+static inline char* accel_getcwd(int *cwd_len TSRMLS_DC)
+{
+       if (ZCG(cwd)) {
+               *cwd_len = ZCG(cwd_len);
+               return ZCG(cwd);
+       } else {
+        char cwd[MAXPATHLEN + 1];
+
+               if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
+                       return NULL;
+               }
+       *cwd_len = ZCG(cwd_len) = strlen(cwd);
+               ZCG(cwd) = estrndup(cwd, ZCG(cwd_len));
+               return ZCG(cwd);
+       }
+}
+
+/* O+ traks changes of "include_path" directive. It stores all the requested
+ * values in ZCG(include_paths) shared hash table, current value in
+ * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
+ * ZCG(include_path_key).
+ */
+static ZEND_INI_MH(accel_include_path_on_modify)
+{
+       int ret = orig_include_path_on_modify(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
+
+       ZCG(include_path_key) = NULL;
+       if (ret == SUCCESS) {
+               ZCG(include_path) = new_value;
+               if (ZCG(include_path) && *ZCG(include_path)) {
+                       ZCG(include_path_len) = new_value_length;
+
+                       if (ZCG(startup_ok) &&
+                           (ZCG(counted) || ZCSG(accelerator_enabled)) &&
+                           !zend_accel_hash_is_full(&ZCSG(include_paths))) {
+
+                               SHM_UNPROTECT();
+                               zend_shared_alloc_lock(TSRMLS_C);
+
+                               ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
+                           if (!ZCG(include_path_key) &&
+                                   !zend_accel_hash_is_full(&ZCSG(include_paths))) {
+                                       char *key;
+
+                                       key = zend_shared_alloc(ZCG(include_path_len) + 2);
+                                       if (key) {
+                                               memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
+                                               key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
+                                               ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
+                                               zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
+                               }
+                       }
+
+                               zend_shared_alloc_unlock(TSRMLS_C);
+                               SHM_PROTECT();
+                       } else {
+                               ZCG(include_path_check) = 1;
+                       }
+               } else {
+                       ZCG(include_path) = "";
+                       ZCG(include_path_len) = 0;
+               }
+       }
+       return ret;
+}
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+/* Interned strings support */
+static char *orig_interned_strings_start;
+static char *orig_interned_strings_end;
+static const char *(*orig_new_interned_string)(const char *str, int len, int free_src TSRMLS_DC);
+static void (*orig_interned_strings_snapshot)(TSRMLS_D);
+static void (*orig_interned_strings_restore)(TSRMLS_D);
+
+/* O+ disables creation of interned strings by regular PHP compiler, instead,
+ * it creates interned strings in shared memory when saves a script.
+ * Such interned strings are shatred across all PHP processes
+ */
+static const char *accel_new_interned_string_for_php(const char *str, int len, int free_src TSRMLS_DC)
+{
+       return str;
+}
+
+static void accel_interned_strings_snapshot_for_php(TSRMLS_D)
+{
+}
+
+static void accel_interned_strings_restore_for_php(TSRMLS_D)
+{
+}
+
+#ifndef ZTS
+static void accel_interned_strings_restore_state(TSRMLS_D)
+{
+       unsigned int i;
+
+       for (i = 0; i < ZCSG(interned_strings).nTableSize; i++) {
+               ZCSG(interned_strings).arBuckets[i] = ZCSG(interned_strings_saved_state).arBuckets[i];
+               if (ZCSG(interned_strings).arBuckets[i]) {
+                       ZCSG(interned_strings).arBuckets[i]->pLast = NULL;
+               }
+       }
+       ZCSG(interned_strings).pListHead = ZCSG(interned_strings_saved_state).pListHead;
+       ZCSG(interned_strings).pListTail = ZCSG(interned_strings_saved_state).pListTail;
+       if (ZCSG(interned_strings).pListHead) {
+               ZCSG(interned_strings).pListHead->pListLast = NULL;
+       }
+       if (ZCSG(interned_strings).pListTail) {
+               ZCSG(interned_strings).pListTail->pListNext = NULL;
+       }
+       ZCSG(interned_strings_top) = ZCSG(interned_strings_saved_state).top;
+}
+
+static void accel_interned_strings_save_state(TSRMLS_D)
+{
+       ZCSG(interned_strings_saved_state).arBuckets = (Bucket**)zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
+       if (!ZCSG(interned_strings_saved_state).arBuckets) {
+               zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+       }
+       memcpy(ZCSG(interned_strings_saved_state).arBuckets, ZCSG(interned_strings).arBuckets, ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
+       ZCSG(interned_strings_saved_state).pListHead = ZCSG(interned_strings).pListHead;
+       ZCSG(interned_strings_saved_state).pListTail = ZCSG(interned_strings).pListTail;
+       ZCSG(interned_strings_saved_state).top = ZCSG(interned_strings_top);
+}
+#endif
+
+const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC)
+{
+/* for now interned strings are supported only for non-ZTS build */
+#ifndef ZTS
+       ulong h;
+       uint nIndex;
+       Bucket *p;
+
+       if (arKey >= ZCSG(interned_strings_start) && arKey < ZCSG(interned_strings_end)) {
+               /* this is already an interned string */
+               return arKey;
+       }
+
+       h = zend_inline_hash_func(arKey, nKeyLength);
+       nIndex = h & ZCSG(interned_strings).nTableMask;
+
+       /* check for existing interned string */
+       p = ZCSG(interned_strings).arBuckets[nIndex];
+       while (p != NULL) {
+               if ((p->h == h) && (p->nKeyLength == (uint)nKeyLength)) {
+                       if (!memcmp(p->arKey, arKey, nKeyLength)) {
+                               if (free_src) {
+                                       efree((char*)arKey);
+                               }
+                               return p->arKey;
+                       }
+               }
+               p = p->pNext;
+       }
+
+       if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >=
+           ZCSG(interned_strings_end)) {
+           /* no memory, return the same non-interned string */
+               return arKey;
+       }
+
+       /* create new interning string in shared interned strings buffer */
+       p = (Bucket *) ZCSG(interned_strings_top);
+       ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength);
+
+       p->arKey = (char*)(p+1);
+       memcpy((char*)p->arKey, arKey, nKeyLength);
+       p->nKeyLength = nKeyLength;
+       p->h = h;
+       p->pData = &p->pDataPtr;
+       p->pDataPtr = p;
+
+       p->pNext = ZCSG(interned_strings).arBuckets[nIndex];
+       p->pLast = NULL;
+       if (p->pNext) {
+               p->pNext->pLast = p;
+       }
+       ZCSG(interned_strings).arBuckets[nIndex] = p;
+
+       p->pListLast = ZCSG(interned_strings).pListTail;
+       ZCSG(interned_strings).pListTail = p;
+       p->pListNext = NULL;
+       if (p->pListLast != NULL) {
+               p->pListLast->pListNext = p;
+       }
+       if (!ZCSG(interned_strings).pListHead) {
+               ZCSG(interned_strings).pListHead = p;
+       }
+
+       ZCSG(interned_strings).nNumOfElements++;
+
+       if (free_src) {
+               efree((char*)arKey);
+       }
+
+       return p->arKey;
+#else
+       return arKey;
+#endif
+}
+
+#ifndef ZTS
+/* Copy PHP interned strings from PHP process memory into the shared memory */
+static void accel_use_shm_interned_strings(TSRMLS_D)
+{
+       Bucket *p, *q;
+
+       /* function table hash keys */
+       p = CG(function_table)->pListHead;
+       while (p) {
+               if (p->nKeyLength) {
+                       p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
+               }
+               p = p->pListNext;
+       }
+
+       /* class table hash keys, class names, properties, methods, constants, etc */
+       p = CG(class_table)->pListHead;
+       while (p) {
+               zend_class_entry *ce = (zend_class_entry*)(p->pDataPtr);
+
+               if (p->nKeyLength) {
+                       p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
+               }
+
+               if (ce->name) {
+                       ce->name = accel_new_interned_string(ce->name, ce->name_length+1, 0 TSRMLS_CC);
+               }
+
+               q = ce->properties_info.pListHead;
+               while (q) {
+                       zend_property_info *info = (zend_property_info*)(q->pData);
+
+                       if (q->nKeyLength) {
+                               q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
+                       }
+
+                       if (info->name) {
+                               info->name = accel_new_interned_string(info->name, info->name_length+1, 0 TSRMLS_CC);
+                       }
+
+                       q = q->pListNext;
+               }
+
+               q =     ce->function_table.pListHead;
+               while (q) {
+                       if (q->nKeyLength) {
+                               q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
+                       }
+                       q = q->pListNext;
+               }
+
+               q =     ce->constants_table.pListHead;
+               while (q) {
+                       if (q->nKeyLength) {
+                               q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
+                       }
+                       q = q->pListNext;
+               }
+
+               p = p->pListNext;
+       }
+
+       /* constant hash keys */
+       p = EG(zend_constants)->pListHead;
+       while (p) {
+               if (p->nKeyLength) {
+                       p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
+               }
+               p = p->pListNext;
+       }
+
+       /* auto globals hash keys and names */
+       p = CG(auto_globals)->pListHead;
+       while (p) {
+               zend_auto_global *auto_global = (zend_auto_global*)p->pData;
+
+               auto_global->name = accel_new_interned_string(auto_global->name, auto_global->name_len + 1, 0 TSRMLS_CC);
+               if (p->nKeyLength) {
+                       p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
+               }
+               p = p->pListNext;
+       }
+}
+#endif
+#endif
+
+static inline void accel_restart_enter(TSRMLS_D)
+{
+#ifdef ZEND_WIN32
+       INCREMENT(restart_in);
+#else
+       static const FLOCK_STRUCTURE(restart_in_progress, F_WRLCK, SEEK_SET, 2, 1);
+
+       if (fcntl(lock_file, F_SETLK, &restart_in_progress)==-1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1):  %s (%d)", strerror(errno), errno);
+       }
+#endif
+       ZCSG(restart_in_progress) = 1;
+}
+
+static inline void accel_restart_leave(TSRMLS_D)
+{
+       ZCSG(restart_in_progress) = 0;
+#ifdef ZEND_WIN32
+       DECREMENT(restart_in);
+#else
+       static const FLOCK_STRUCTURE(restart_finished, F_UNLCK, SEEK_SET, 2, 1);
+
+       if (fcntl(lock_file, F_SETLK, &restart_finished)==-1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1):  %s (%d)", strerror(errno), errno);
+       }
+#endif
+}
+
+static inline int accel_restart_is_active(TSRMLS_D)
+{
+       if(ZCSG(restart_in_progress)) {
+#ifndef ZEND_WIN32
+               FLOCK_STRUCTURE(restart_check, F_WRLCK, SEEK_SET, 2, 1);
+
+               if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
+                       zend_accel_error(ACCEL_LOG_DEBUG, "RestartC:  %s (%d)", strerror(errno), errno);
+                       return FAILURE;
+               }
+               if (restart_check.l_type == F_UNLCK) {
+                       ZCSG(restart_in_progress) = 0;
+                       return 0;
+               } else {
+                       return 1;
+               }
+#else
+               return LOCKVAL(restart_in) != 0;
+#endif
+       }
+       return 0;
+}
+
+/* Creates a read lock for SHM access */
+static inline void accel_activate_add(TSRMLS_D)
+{
+#ifdef ZEND_WIN32
+       INCREMENT(mem_usage);
+#else
+       static const FLOCK_STRUCTURE(mem_usage_lock, F_RDLCK, SEEK_SET, 1, 1);
+
+       if (fcntl(lock_file, F_SETLK, &mem_usage_lock)==-1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1):  %s (%d)", strerror(errno), errno);
+       }
+#endif
+}
+
+/* Releases a lock for SHM access */
+static inline void accel_deactivate_sub(TSRMLS_D)
+{
+#ifdef ZEND_WIN32
+       if (ZCG(counted)) {
+               DECREMENT(mem_usage);
+               ZCG(counted) = 0;
+       }
+#else
+       static const FLOCK_STRUCTURE(mem_usage_unlock, F_UNLCK, SEEK_SET, 1, 1);
+
+       if (fcntl(lock_file, F_SETLK, &mem_usage_unlock)==-1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1):  %s (%d)", strerror(errno), errno);
+       }
+#endif
+}
+
+static inline void accel_unlock_all(TSRMLS_D)
+{
+#ifdef ZEND_WIN32
+       accel_deactivate_sub(TSRMLS_C);
+#else
+       static const FLOCK_STRUCTURE(mem_usage_unlock_all, F_UNLCK, SEEK_SET, 0, 0);
+
+       if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all)==-1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll:  %s (%d)", strerror(errno), errno);
+       }
+#endif
+}
+
+#ifndef ZEND_WIN32
+static inline void kill_all_lockers(struct flock *mem_usage_check)
+{
+       int tries = 10;
+
+       /* so that other process won't try to force while we are busy cleaning up */
+       ZCSG(force_restart_time) = 0;
+       while (mem_usage_check->l_pid > 0) {
+               while (tries--) {
+                       zend_accel_error(ACCEL_LOG_INFO, "Killed locker %d", mem_usage_check->l_pid);
+                       if (kill(mem_usage_check->l_pid, SIGKILL)) {
+                               break;
+                       }
+                       /* give it a chance to die */
+                       usleep(20000);
+                       if (kill(mem_usage_check->l_pid, 0)) {
+                               /* can't kill it */
+                               break;
+                       }
+                       usleep(10000);
+               }
+               if (!tries) {
+                       zend_accel_error(ACCEL_LOG_INFO, "Can't kill %d after 20 tries!", mem_usage_check->l_pid);
+                       ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
+               }
+
+               mem_usage_check->l_type = F_WRLCK;
+               mem_usage_check->l_whence = SEEK_SET;
+               mem_usage_check->l_start = 1;
+               mem_usage_check->l_len = 1;
+               mem_usage_check->l_pid = -1;
+               if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) {
+                       zend_accel_error(ACCEL_LOG_DEBUG, "KLockers:  %s (%d)", strerror(errno), errno);
+                       break;
+               }
+
+               if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) {
+                       break;
+               }
+       }
+}
+#endif
+
+static inline int accel_is_inactive(TSRMLS_D)
+{
+#ifdef ZEND_WIN32
+       if (LOCKVAL(mem_usage) == 0) {
+               return SUCCESS;
+       }
+#else
+       FLOCK_STRUCTURE(mem_usage_check, F_WRLCK, SEEK_SET, 1, 1);
+
+       mem_usage_check.l_pid = -1;
+       if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC:  %s (%d)", strerror(errno), errno);
+               return FAILURE;
+       }
+       if (mem_usage_check.l_type == F_UNLCK) {
+               return SUCCESS;
+       }
+
+       if (ZCG(accel_directives).force_restart_timeout
+               && ZCSG(force_restart_time)
+               && time(NULL)>=ZCSG(force_restart_time)) {
+               zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
+               kill_all_lockers(&mem_usage_check);
+
+               return FAILURE; /* next request should be able to restart it */
+       }
+#endif
+
+       return FAILURE;
+}
+
+static int zend_get_stream_timestamp(const char *filename, struct stat *statbuf TSRMLS_DC)
+{
+       php_stream_wrapper *wrapper;
+       php_stream_statbuf stream_statbuf;
+
+       if(!filename) {
+               return FAILURE;
+       }
+
+       wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC);
+       if(!wrapper) {
+
+               return FAILURE;
+       }
+       if(!wrapper->wops || !wrapper->wops->url_stat) {
+
+               statbuf->st_mtime = 1;
+               return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */
+       }
+
+       if(wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL TSRMLS_CC) != 0) {
+               return FAILURE;
+       }
+       *statbuf = stream_statbuf.sb;
+       return SUCCESS;
+}
+
+#if ZEND_WIN32
+static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle)
+{
+       static unsigned __int64 utc_base = 0;
+       static FILETIME utc_base_ft;
+       WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+       if(!file_handle->opened_path) {
+               return 0;
+       }
+
+       if (!utc_base) {
+               SYSTEMTIME st;
+
+               st.wYear = 1970;
+               st.wMonth = 1;
+               st.wDay = 1;
+               st.wHour = 0;
+               st.wMinute = 0;
+               st.wSecond = 0;
+               st.wMilliseconds = 0;
+
+               SystemTimeToFileTime (&st, &utc_base_ft);
+               utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime;
+    }
+
+       if(GetFileAttributesEx(file_handle->opened_path, GetFileExInfoStandard, &fdata) != 0) {
+               unsigned __int64 ftime;
+
+               if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0)
+                       return 0;
+
+               ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base;
+               ftime /= 10000000L;
+
+               return (accel_time_t)ftime;
+       }
+       return 0;
+}
+#endif
+
+static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle TSRMLS_DC)
+{
+       struct stat statbuf;
+
+#ifdef ZEND_WIN32
+       accel_time_t res;
+
+       res = zend_get_file_handle_timestamp_win(file_handle);
+       if(res) {
+               return res;
+       }
+#endif
+
+       switch (file_handle->type) {
+               case ZEND_HANDLE_FD:
+                       if (fstat(file_handle->handle.fd, &statbuf)==-1) {
+                               return 0;
+                       }
+                       break;
+               case ZEND_HANDLE_FP:
+                       if (fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
+                               if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) {
+                                       return 0;
+                               }
+                       }
+                       break;
+               case ZEND_HANDLE_FILENAME:
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               case ZEND_HANDLE_MAPPED:
+#endif
+                       {
+                               char *file_path = file_handle->opened_path;
+
+                               if(file_path) {
+                                       if (is_stream_path(file_path)) {
+                                               if (zend_get_stream_timestamp(file_path, &statbuf TSRMLS_CC) == SUCCESS) {
+                                                       break;
+                                               }
+                                       }
+                                       if (VCWD_STAT(file_path, &statbuf) !=-1) {
+                                               break;
+                                       }
+                               }
+
+                               if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) {
+                                       return 0;
+                               }
+                               break;
+                       }
+               case ZEND_HANDLE_STREAM:
+                       {
+                               php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
+                               php_stream_statbuf sb;
+                               int er = EG(error_reporting);
+
+                               EG(error_reporting) = 0;
+                               if (!stream ||
+                                   !stream->ops ||
+                                   !stream->ops->stat ||
+                                   stream->ops->stat(stream, &sb TSRMLS_CC) != 0) {
+                                       EG(error_reporting) = er;
+                                       return 0;
+                               }
+                               EG(error_reporting) = er;
+                               statbuf = sb.sb;
+                       }
+                       break;
+
+               default:
+                       return 0;
+       }
+
+       return statbuf.st_mtime;
+}
+
+static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC)
+{
+       zend_file_handle ps_handle;
+
+       /** check that the persistant script is indeed the same file we cached
+        * (if part of the path is a symlink than it possible that the user will change it)
+        * See bug #15140
+        */
+       if (file_handle->opened_path) {
+               if (strcmp(persistent_script->full_path,file_handle->opened_path)!=0) {
+                       return FAILURE;
+               }
+       } else {
+               char actualpath [MAXPATHLEN+1];
+               char *full_path_ptr;
+
+               full_path_ptr = VCWD_REALPATH(file_handle->filename, actualpath);
+               if (full_path_ptr && strcmp(persistent_script->full_path,full_path_ptr)!=0) {
+                       return FAILURE;
+               }
+       }
+
+       if (persistent_script->timestamp == 0) {
+               return FAILURE;
+       }
+
+       if (zend_get_file_handle_timestamp(file_handle TSRMLS_CC) == persistent_script->timestamp) {
+               return SUCCESS;
+       }
+
+       ps_handle.type = ZEND_HANDLE_FILENAME;
+       ps_handle.filename = persistent_script->full_path;
+       ps_handle.opened_path = persistent_script->full_path;
+
+       if (zend_get_file_handle_timestamp(&ps_handle TSRMLS_CC) == persistent_script->timestamp) {
+               return SUCCESS;
+       }
+
+       return FAILURE;
+}
+
+static void zend_accel_schedule_restart_if_necessary(TSRMLS_D)
+{
+       if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
+               zend_accel_schedule_restart(TSRMLS_C);
+       }
+}
+
+static inline int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC)
+{
+       if (persistent_script->dynamic_members.revalidate >= ZCSG(revalidate_at)) {
+               return SUCCESS;
+       } else if (do_validate_timestamps(persistent_script, file_handle TSRMLS_CC)==FAILURE) {
+               return FAILURE;
+       } else {
+               persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at);
+               return SUCCESS;
+       }
+}
+
+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 */
+char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC)
+{
+    int key_length;
+
+    /* CWD and include_path don't matter for absolute file names and streams */
+    if (ZCG(accel_directives).use_cwd &&
+        !IS_ABSOLUTE_PATH(file_handle->filename, path_length) &&
+        !is_stream_path(file_handle->filename)) {
+        char *include_path=NULL;
+        int include_path_len=0;
+        const char *parent_script = NULL;
+        int parent_script_len=0;
+        int cur_len=0;
+        int cwd_len;
+        char *cwd;
+
+        if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) == NULL) {
+            /* we don't handle this well for now. */
+            zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set zend_optimizerplus.use_cwd to 0 in ini file", file_handle->filename, errno);
+            if(file_handle->opened_path) {
+                cwd = file_handle->opened_path;
+                       cwd_len = strlen(cwd);
+            } else {
+                               ZCG(key_len) = 0;
+                return NULL;
+            }
+        }
+
+               if (ZCG(include_path_key)) {
+                       include_path = ZCG(include_path_key);
+                       include_path_len = 1;
+               } else {
+               include_path = ZCG(include_path);
+           include_path_len = ZCG(include_path_len);
+                       if (ZCG(include_path_check) &&
+                           ZCG(startup_ok) &&
+                           (ZCG(counted) || ZCSG(accelerator_enabled)) &&
+                           !zend_accel_hash_is_full(&ZCSG(include_paths))) {
+
+                               SHM_UNPROTECT();
+                               zend_shared_alloc_lock(TSRMLS_C);
+
+                               ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
+                               if (ZCG(include_path_key)) {
+                                       include_path = ZCG(include_path_key);
+                                       include_path_len = 1;
+                               } else if (!zend_accel_hash_is_full(&ZCSG(include_paths))) {
+                                       char *key;
+
+                                       key = zend_shared_alloc(ZCG(include_path_len) + 2);
+                                       if (key) {
+                                               memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
+                                               key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
+                                               ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
+                                               zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
+                                               include_path = ZCG(include_path_key);
+                                               include_path_len = 1;
+                               }
+               }
+
+                               zend_shared_alloc_unlock(TSRMLS_C);
+                               SHM_PROTECT();
+                       }
+               }
+
+        /* Here we add to the key the parent script directory,
+           since fopen_wrappers from version 4.0.7 use current script's path
+           in include path too.
+        */
+        if (EG(in_execution) &&
+            (parent_script = zend_get_executed_filename(TSRMLS_C)) != NULL &&
+               parent_script[0] != '[') {
+
+            parent_script_len = strlen(parent_script);
+            while ((--parent_script_len > 0) && !IS_SLASH(parent_script[parent_script_len]));
+        }
+
+        /* Calculate key length */
+        key_length = cwd_len+path_length+include_path_len+2;
+        if (parent_script_len) {
+            key_length += parent_script_len+1;
+        }
+
+        /* Generate key
+         * Note - the include_path must be the last element in the key,
+         * since in itself, it may include colons (which we use to separate
+         * different components of the key)
+         */
+               if ((size_t)key_length >= sizeof(ZCG(key))) {
+                       ZCG(key_len) = 0;
+                       return NULL;
+               }
+               memcpy(ZCG(key), cwd, cwd_len);
+               ZCG(key)[cwd_len] = ':';
+
+               memcpy(ZCG(key)+cwd_len+1, file_handle->filename, path_length);
+
+               ZCG(key)[cwd_len+1+path_length] = ':';
+
+        cur_len = cwd_len+1+path_length+1;
+
+        if (parent_script_len) {
+                       memcpy(ZCG(key)+cur_len, parent_script, parent_script_len);
+            cur_len += parent_script_len;
+                       ZCG(key)[cur_len] = ':';
+            cur_len++;
+        }
+               memcpy(ZCG(key)+cur_len, include_path, include_path_len);
+               ZCG(key)[key_length] = '\0';
+    } else {
+        /* not use_cwd */
+        key_length = path_length;
+               if ((size_t)key_length >= sizeof(ZCG(key))) {
+                       ZCG(key_len) = 0;
+                       return NULL;
+               }
+               memcpy(ZCG(key), file_handle->filename, key_length+1);
+    }
+
+       *key_len = ZCG(key_len) = key_length;
+       return ZCG(key);
+}
+
+static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int *key_len TSRMLS_DC)
+{
+       return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC);
+}
+
+/* Adds another key for existing cached script */
+static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC)
+{
+       if (!zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) {
+               if (zend_accel_hash_is_full(&ZCSG(hash))) {
+                       zend_accel_error(ACCEL_LOG_DEBUG,"No more entries in hash table!");
+                       ZSMMG(memory_exhausted) = 1;
+               } else {
+                       char *new_key = zend_shared_alloc(key_length+1);
+                       if (new_key) {
+                               memcpy(new_key, key, key_length+1);
+                               zend_accel_hash_update(&ZCSG(hash), new_key, key_length+1, 1, bucket);
+                       } else {
+                               zend_accel_error(ACCEL_LOG_DEBUG,"No more memory!");
+                               ZSMMG(memory_exhausted) = 1;
+                       }
+               }
+       }
+}
+
+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 TSRMLS_DC)
+{
+       zend_accel_hash_entry *bucket;
+       uint memory_used;
+
+       /* Check if script may be stored in shared memory */
+       if(!zend_accel_script_persistable(new_persistent_script)) {
+               return new_persistent_script;
+       }
+
+       /* exclusive lock */
+       zend_shared_alloc_lock(TSRMLS_C);
+
+       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(TSRMLS_C);
+               zend_shared_alloc_unlock(TSRMLS_C);
+               return new_persistent_script;
+       }
+
+       /* 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), new_persistent_script->full_path, new_persistent_script->full_path_len+1);
+       if (bucket) {
+               zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
+
+               if (!existing_persistent_script->corrupted) {
+                       if (!ZCG(accel_directives).validate_timestamps ||
+                           (new_persistent_script->timestamp == existing_persistent_script->timestamp)) {
+                               zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
+                       }
+                       zend_shared_alloc_unlock(TSRMLS_C);
+                       return new_persistent_script;
+               }
+       }
+
+       /* Calculate the required memory size */
+       memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length TSRMLS_CC);
+
+       /* Allocate shared memory */
+       ZCG(mem) = zend_shared_alloc(memory_used);
+       if (!ZCG(mem)) {
+               zend_accel_error(ACCEL_LOG_DEBUG, "No more memory!");
+               zend_accel_schedule_restart_if_necessary(TSRMLS_C);
+               if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) {
+                       ZSMMG(memory_exhausted) = 1;
+               }
+               zend_shared_alloc_unlock(TSRMLS_C);
+               return new_persistent_script;
+       }
+
+       /* cleanup after calculation */
+       new_persistent_script->mem  = ZCG(mem);
+       new_persistent_script->size = memory_used;
+
+       /* Copy into shared memory */
+       new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length TSRMLS_CC);
+
+       /* 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,
+                       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);
+
+       /* store script structure in the hash table */
+       bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len+1, 0, new_persistent_script);
+       if (bucket &&
+           (new_persistent_script->full_path_len != key_length ||
+            memcmp(new_persistent_script->full_path, key, key_length) != 0)) {
+               /* link key to the same persistent script in hash table */
+               if (!zend_accel_hash_update(&ZCSG(hash), key, key_length+1, 1, bucket)) {
+                       zend_accel_error(ACCEL_LOG_DEBUG,"No more entries in hash table!");
+                       ZSMMG(memory_exhausted) = 1;
+               }
+       }
+
+       new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
+
+       zend_shared_alloc_unlock(TSRMLS_C);
+
+       *from_shared_memory = 1;
+       return new_persistent_script;
+}
+
+static const struct jit_auto_global_info
+{
+    const char *name;
+    size_t len;
+} jit_auto_globals_info[] = {
+    { "_SERVER",  sizeof("_SERVER")},
+    { "_ENV",     sizeof("_ENV")},
+    { "_REQUEST", sizeof("_REQUEST")},
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+    { "GLOBALS",  sizeof("GLOBALS")},
+#endif
+};
+
+static int zend_accel_get_auto_globals(TSRMLS_D)
+{
+       int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
+       int n = 1;
+       zval **res;
+       int mask = 0;
+
+       for (i = 0; i < ag_size ; i++) {
+               if (zend_hash_find(&EG(symbol_table), jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, (void *)&res) == SUCCESS) {
+                       mask |= n;
+               }
+               n += n;
+       }
+       return mask;
+}
+
+static void zend_accel_set_auto_globals(int mask TSRMLS_DC)
+{
+       int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
+       int n = 1;
+
+       for (i = 0; i < ag_size ; i++) {
+               if (mask & n) {
+                       zend_is_auto_global(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len - 1 TSRMLS_CC);
+               }
+               n += n;
+       }
+}
+
+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 TSRMLS_DC)
+{
+       zend_persistent_script *new_persistent_script;
+       zend_op_array *orig_active_op_array;
+       HashTable *orig_function_table, *orig_class_table;
+       zval *orig_user_error_handler;
+       zend_op_array *op_array;
+       int do_bailout = 0;
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       zend_uint orig_compiler_options = 0;
+#endif
+
+    /* Try to open file */
+    if (file_handle->type == ZEND_HANDLE_FILENAME) {
+        if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == SUCCESS) {
+               /* key may be changed by zend_stream_open_function() */
+               if (key == ZCG(key)) {
+                       key_length = ZCG(key_len);
+               }
+        } else {
+                       *op_array_p = NULL;
+                       if (type==ZEND_REQUIRE) {
+                               zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
+                               zend_bailout();
+                       } else {
+                               zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
+                       }
+                       return NULL;
+       }
+    }
+
+       /* check blacklist right after ensuring that file was opened */
+       if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path)) {
+               ZCSG(blacklist_misses)++;
+               *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+               return NULL;
+       }
+
+       new_persistent_script = create_persistent_script();
+
+       /* Save the original values for the op_array, function table and class table */
+       orig_active_op_array = CG(active_op_array);
+       orig_function_table = CG(function_table);
+       orig_class_table = CG(class_table);
+       orig_user_error_handler = EG(user_error_handler);
+
+       /* Override them with ours */
+       CG(function_table) = &ZCG(function_table);
+       EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
+       EG(user_error_handler) = NULL;
+
+       zend_try {
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               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;
+#endif
+               op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               CG(compiler_options) = orig_compiler_options;
+#endif
+       } zend_catch {
+               op_array = NULL;
+               do_bailout = 1;
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               CG(compiler_options) = orig_compiler_options;
+#endif
+       } zend_end_try();
+
+       /* Restore originals */
+       CG(active_op_array) = orig_active_op_array;
+       CG(function_table) = orig_function_table;
+       EG(class_table) = CG(class_table) = orig_class_table;
+       EG(user_error_handler) = orig_user_error_handler;
+
+       if (!op_array) {
+               /* compilation failed */
+               free_persistent_script(new_persistent_script, 1);
+               zend_accel_free_user_functions(&ZCG(function_table) TSRMLS_CC);
+               if(do_bailout) {
+                       zend_bailout();
+               }
+               return NULL;
+       }
+
+       /* Build the persistent_script structure.
+          Here we aren't sure we would store it, but we will need it
+          further anyway.
+       */
+       zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table TSRMLS_CC);
+       new_persistent_script->main_op_array = *op_array;
+
+       efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
+
+    /* 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 varaibles before execution */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (PG(auto_globals_jit)) {
+#else
+       if ((PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays))) {
+#endif
+               new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C);
+    }
+
+       if (ZCG(accel_directives).validate_timestamps) {
+               /* Obtain the file timestamps, *before* actually compiling them,
+                * otherwise we have a race-condition.
+                */
+               new_persistent_script->timestamp = zend_get_file_handle_timestamp(file_handle TSRMLS_CC);
+
+               /* If we can't obtain a timestamp (that means file is possibly socket)
+                *  we return it but do not persist it
+                */
+               if (new_persistent_script->timestamp == 0) {
+                       return new_persistent_script;
+               }
+               new_persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at);
+       }
+
+       if (file_handle->opened_path) {
+               new_persistent_script->full_path_len = strlen(file_handle->opened_path);
+               new_persistent_script->full_path = estrndup(file_handle->opened_path, new_persistent_script->full_path_len);
+       } else {
+               new_persistent_script->full_path_len = strlen(file_handle->filename);
+               new_persistent_script->full_path = estrndup(file_handle->filename, new_persistent_script->full_path_len);
+       }
+       new_persistent_script->hash_value = zend_hash_func(new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
+
+       /* Now persistent_script structure is ready in process memory */
+       return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory TSRMLS_CC);
+}
+
+/* zend_compile() replacement */
+static zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
+{
+       zend_persistent_script *persistent_script = NULL;
+       char *key = NULL;
+       int key_length;
+       int from_shared_memory; /* if the script we've got is stored in SHM */
+
+       if (!file_handle->filename ||
+               !ZCG(startup_ok) ||
+               (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
+           CG(interactive) ||
+           (ZCSG(restart_in_progress) && accel_restart_is_active(TSRMLS_C))) {
+               /* The Accelerator is disabled, act as if without the Accelerator */
+               return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+       }
+
+       /* Make sure we only increase the currently running processes semaphore
+     * once each execution (this function can be called more than once on
+     * each execution)
+     */
+       if (!ZCG(counted)) {
+               ZCG(counted) = 1;
+               accel_activate_add(TSRMLS_C);
+       }
+
+       /* In case this callback is called from include_once, require_once or it's
+        * a main FastCGI request, the key must be already calculated, and cached
+        * persistent script already found */
+       if ((EG(opline_ptr) == NULL &&
+            ZCG(cache_opline) == NULL &&
+            file_handle->filename == SG(request_info).path_translated &&
+            ZCG(cache_persistent_script)) ||
+           (EG(opline_ptr) && *EG(opline_ptr) &&
+            *EG(opline_ptr) == ZCG(cache_opline) &&
+            (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+            ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
+             (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
+#else
+            ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
+             (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
+#endif
+               if (!ZCG(key_len)) {
+                       return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+               }
+               /* persistent script was already found by overrifen open() or
+                * resolve_path() callbacks */
+               persistent_script = ZCG(cache_persistent_script);
+               key = ZCG(key);
+               key_length = ZCG(key_len);
+       } else {
+               /* try to find cached script by key */
+               if ((key = accel_make_persistent_key(file_handle, &key_length TSRMLS_CC)) == NULL) {
+                       return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+               }
+               persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1);
+               if (!persistent_script) {
+                       /* try to find cached script by full real path */
+                       zend_accel_hash_entry *bucket;
+
+                       /* open file to resolve the path */
+                   if (file_handle->type == ZEND_HANDLE_FILENAME &&
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+                       accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
+#else
+                       zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
+#endif
+                               if (type==ZEND_REQUIRE) {
+                                       zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
+                                       zend_bailout();
+                               } else {
+                                       zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
+                               }
+                               return NULL;
+                   }
+
+                       if (file_handle->opened_path &&
+                           (bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path, strlen(file_handle->opened_path)+1)) != NULL) {
+
+                               persistent_script = (zend_persistent_script *)bucket->data;
+                               if (!ZCG(accel_directives).revalidate_path &&
+                                   !persistent_script->corrupted) {
+                               SHM_UNPROTECT();
+                                       zend_shared_alloc_lock(TSRMLS_C);
+                                       zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
+                                       zend_shared_alloc_unlock(TSRMLS_C);
+                               SHM_PROTECT();
+                               }
+                       }
+               }
+       }
+
+       /* clear cache */
+       ZCG(cache_opline) = NULL;
+       ZCG(cache_persistent_script) = NULL;
+
+       if (persistent_script && persistent_script->corrupted) {
+               persistent_script = NULL;
+       }
+
+       SHM_UNPROTECT();
+
+       /* If script is found then validate_timestamps if option is enabled */
+       if (persistent_script && ZCG(accel_directives).validate_timestamps) {
+               if (validate_timestamp_and_record(persistent_script, file_handle TSRMLS_CC)==FAILURE) {
+                       zend_shared_alloc_lock(TSRMLS_C);
+                       if (!persistent_script->corrupted) {
+                               persistent_script->corrupted = 1;
+                               persistent_script->timestamp = 0;
+                               ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
+                       }
+                       zend_shared_alloc_unlock(TSRMLS_C);
+                       persistent_script = NULL;
+               }
+       }
+
+       /* if turned on - check the compiled script ADLER32 checksum */
+       if (persistent_script && ZCG(accel_directives).consistency_checks
+               && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
+
+               unsigned int checksum = zend_accel_script_checksum(persistent_script);
+               if (checksum != persistent_script->dynamic_members.checksum ) {
+                       /* The checksum is wrong */
+                       zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%0.8X, found=0x%0.8X",
+                                                        persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
+                       zend_shared_alloc_lock(TSRMLS_C);
+                       if (!persistent_script->corrupted) {
+                               persistent_script->corrupted = 1;
+                               persistent_script->timestamp = 0;
+                               ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
+                       }
+                       zend_shared_alloc_unlock(TSRMLS_C);
+                       persistent_script = NULL;
+               }
+       }
+
+       /* If script was not found or invalidated by validate_timestamps */
+       if (!persistent_script) {
+               zend_op_array *op_array;
+
+               /* Cache miss.. */
+               ZCSG(misses)++;
+
+               /* No memory left. Behave like without the Accelerator */
+               if (ZSMMG(memory_exhausted)) {
+                       SHM_PROTECT();
+                       return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
+               }
+
+               /* Try and cache the script and assume that it is returned from_shared_memory.
+         * 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 TSRMLS_CC);
+
+               /* Something went wrong during compilation, returning NULL */
+               if (!persistent_script) {
+                       SHM_PROTECT();
+                       return op_array; /* Presently always NULL, but not necessary in the future */
+               }
+       } else {
+
+#if !ZEND_WIN32
+               ZCSG(hits)++; /* TBFixed: may lose one hit */
+               persistent_script->dynamic_members.hits++; /* see above */
+#else
+               InterlockedIncrement(&ZCSG(hits));
+               InterlockedIncrement(&persistent_script->dynamic_members.hits);
+#endif
+
+               /* see bug #15471 (old BTS) */
+               if (persistent_script->full_path) {
+                       if (!EG(opline_ptr) || !*EG(opline_ptr) ||
+                           (*EG(opline_ptr))->opcode != ZEND_INCLUDE_OR_EVAL ||
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                           ((*EG(opline_ptr))->extended_value != ZEND_INCLUDE_ONCE &&
+                            (*EG(opline_ptr))->extended_value != ZEND_REQUIRE_ONCE)) {
+#else
+                           ((*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_INCLUDE_ONCE &&
+                            (*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_REQUIRE_ONCE)) {
+#endif
+                               void *dummy = (void *) 1;
+
+                               zend_hash_quick_add(&EG(included_files), persistent_script->full_path, persistent_script->full_path_len+1, persistent_script->hash_value, &dummy, sizeof(void *), NULL);
+                       }
+               }
+               zend_file_handle_dtor(file_handle TSRMLS_CC);
+               from_shared_memory = 1;
+       }
+
+       persistent_script->dynamic_members.last_used = ZCG(request_time);
+
+       SHM_PROTECT();
+
+    /* Fetch jit auto globals used in the script before execution */
+    if (persistent_script->ping_auto_globals_mask) {
+               zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask TSRMLS_CC);
+       }
+
+       return zend_accel_load_script(persistent_script, from_shared_memory TSRMLS_CC);
+}
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+
+/* Taken from php-5.2.5 because early versions don't have correct version */
+static char *accel_tsrm_realpath(const char *path, int path_len TSRMLS_DC)
+{
+       cwd_state new_state;
+       char *real_path;
+       char *cwd;
+       int cwd_len;
+
+       /* realpath("") returns CWD */
+       if (!*path) {
+               new_state.cwd = (char*)malloc(1);
+               new_state.cwd[0] = '\0';
+               new_state.cwd_length = 0;
+           if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) {
+                   path = cwd;
+               }
+       } else if (!IS_ABSOLUTE_PATH(path, path_len) &&
+           (cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) {
+               new_state.cwd = zend_strndup(cwd, cwd_len);
+               new_state.cwd_length = cwd_len;
+       } else {
+               new_state.cwd = (char*)malloc(1);
+               new_state.cwd[0] = '\0';
+               new_state.cwd_length = 0;
+       }
+
+#ifndef CWD_REALPATH
+# define CWD_REALPATH 2
+#endif
+       if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
+               free(new_state.cwd);
+               return NULL;
+       }
+
+       real_path = emalloc(new_state.cwd_length+1);
+       memcpy(real_path, new_state.cwd, new_state.cwd_length+1);
+       free(new_state.cwd);
+       return real_path;
+}
+
+static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC)
+{
+       char *resolved_path;
+       char trypath[MAXPATHLEN];
+       const char *ptr, *end;
+       int len;
+
+       if (!filename) {
+               return NULL;
+       }
+
+       if (*filename == '.' ||
+           IS_ABSOLUTE_PATH(filename, filename_length) ||
+           !path ||
+           !*path) {
+               return accel_tsrm_realpath(filename, filename_length TSRMLS_CC);
+       }
+
+       ptr = path;
+       while (*ptr) {
+               for (end = ptr; *end && *end != DEFAULT_DIR_SEPARATOR; end++);
+               len = end-ptr;
+               if (*end) end++;
+               if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
+                       ptr = end;
+                       continue;
+               }
+               memcpy(trypath, ptr, len);
+               trypath[len] = '/';
+               memcpy(trypath+len+1, filename, filename_length+1);
+               ptr = end;
+               if ((resolved_path = accel_tsrm_realpath(trypath, len+1+filename_length TSRMLS_CC)) != NULL) {
+                       return resolved_path;
+               }
+       } /* end provided path */
+
+       /* check in calling scripts' current working directory as a fall back case
+        */
+       if (EG(in_execution)) {
+               char *exec_fname = zend_get_executed_filename(TSRMLS_C);
+               int exec_fname_length = strlen(exec_fname);
+
+               while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
+               if (exec_fname && exec_fname[0] != '[' &&
+                   exec_fname_length > 0 &&
+                   exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
+                       memcpy(trypath, exec_fname, exec_fname_length + 1);
+                       memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
+                       return accel_tsrm_realpath(trypath, exec_fname_length+1+filename_length TSRMLS_CC);
+               }
+       }
+
+       return NULL;
+}
+
+/* zend_stream_open_function() replacement for PHP 5.2 */
+static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC)
+{
+       if (ZCG(startup_ok) &&
+           (ZCG(counted) || ZCSG(accelerator_enabled)) &&
+           !CG(interactive) &&
+           !ZCSG(restart_in_progress)) {
+
+               if (EG(opline_ptr) && *EG(opline_ptr)) {
+                       zend_op *opline = *EG(opline_ptr);
+
+            if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
+                (opline->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
+                 opline->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE)) {
+                               /* we are in include_once */
+                               char *key = NULL;
+                               int key_length;
+                               char *resolved_path;
+                               zend_accel_hash_entry *bucket;
+                               zend_persistent_script *persistent_script;
+                               int filename_len;
+
+                               if (opline->op1.op_type == IS_CONST) {
+                                       filename_len = Z_STRLEN(opline->op1.u.constant);
+                               } else {
+                                       filename_len = strlen(filename);
+                               }
+                               handle->filename = (char*)filename;
+                               handle->free_filename = 0;
+                               handle->opened_path = NULL;
+
+                           /* Check if requestd file already cached (by full name) */
+                           if (IS_ABSOLUTE_PATH(filename, filename_len) &&
+                                   (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL &&
+                                   !persistent_script->corrupted) {
+
+                                       handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                                       handle->type = ZEND_HANDLE_FILENAME;
+                                       memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1);
+                                       ZCG(key_len) = persistent_script->full_path_len;
+                                       ZCG(cache_opline) = opline;
+                                       ZCG(cache_persistent_script) = persistent_script;
+                                       return SUCCESS;
+                           }
+
+                           /* Check if requestd file already cached (by key) */
+                               key = accel_make_persistent_key_ex(handle, filename_len, &key_length TSRMLS_CC);
+                               if (!ZCG(accel_directives).revalidate_path &&
+                                   key &&
+                                       (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) != NULL &&
+                                   !persistent_script->corrupted) {
+
+                                       handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                                       handle->type = ZEND_HANDLE_FILENAME;
+                                       ZCG(cache_opline) = opline;
+                                       ZCG(cache_persistent_script) = persistent_script;
+                                       return SUCCESS;
+                               }
+
+                               /* find the full real path */
+                               resolved_path = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
+
+                           /* Check if requestd file already cached (by real name) */
+                               if (resolved_path &&
+                                   (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path)+1)) != NULL) {
+
+                                       persistent_script = (zend_persistent_script *)bucket->data;
+                                       if (persistent_script && !persistent_script->corrupted) {
+                                               handle->opened_path = resolved_path;
+                                               handle->type = ZEND_HANDLE_FILENAME;
+                                               if (key && !ZCG(accel_directives).revalidate_path) {
+                                                       /* add another "key" for the same bucket */
+                                               SHM_UNPROTECT();
+                                                       zend_shared_alloc_lock(TSRMLS_C);
+                                                       zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
+                                                       zend_shared_alloc_unlock(TSRMLS_C);
+                                               SHM_PROTECT();
+                                               }
+                                               ZCG(cache_opline) = opline;
+                                               ZCG(cache_persistent_script) = persistent_script;
+                                               return SUCCESS;
+                                       }
+                               }
+                               if (resolved_path) {
+                                       efree(resolved_path);
+                               }
+                       }
+               }
+       }
+       ZCG(cache_opline) = NULL;
+       ZCG(cache_persistent_script) = NULL;
+       return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC);
+}
+
+#else
+
+/* zend_stream_open_function() replacement for PHP 5.3 and above */
+static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC)
+{
+       if (ZCG(startup_ok) &&
+           (ZCG(counted) || ZCSG(accelerator_enabled)) &&
+           !CG(interactive) &&
+           !ZCSG(restart_in_progress)) {
+
+               /* check if callback is called from include_once or it's a main request */
+               if ((!EG(opline_ptr) &&
+                    filename == SG(request_info).path_translated) ||
+                   (EG(opline_ptr) &&
+                    *EG(opline_ptr) &&
+             (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+             ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
+              (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
+#else
+              ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
+               (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
+#endif
+
+                       /* we are in include_once or FastCGI request */
+                       zend_persistent_script *persistent_script;
+
+                       handle->filename = (char*)filename;
+                       handle->free_filename = 0;
+
+                       /* check if cached script was already found by resolve_path() */
+                       if ((EG(opline_ptr) == NULL &&
+                            ZCG(cache_opline) == NULL &&
+                            ZCG(cache_persistent_script) != NULL) ||
+                           (EG(opline_ptr) &&
+                            (ZCG(cache_opline) == *EG(opline_ptr)))) {
+                               persistent_script = ZCG(cache_persistent_script);
+                               handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                               handle->type = ZEND_HANDLE_FILENAME;
+                               return SUCCESS;
+#if 0
+                       } else {
+                               /* FIXME: It looks like this part is not needed any more */
+                               int filename_len = strlen(filename);
+
+                           if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
+                                is_stream_path(filename)) &&
+                                   (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL &&
+                                   !persistent_script->corrupted) {
+
+                                       handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                                       handle->type = ZEND_HANDLE_FILENAME;
+                                       memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1);
+                                       ZCG(key_len) = persistent_script->full_path_len;
+                                       ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
+                                       ZCG(cache_persistent_script) = EG(opline_ptr) ? persistent_script : NULL;
+                                       return SUCCESS;
+                           }
+#endif
+                       }
+               }
+       }
+       ZCG(cache_opline) = NULL;
+       ZCG(cache_persistent_script) = NULL;
+       return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC);
+}
+
+/* zend_resolve_path() replacement for PHP 5.3 and above */
+static char* persistent_zend_resolve_path(const char *filename, int filename_len TSRMLS_DC)
+{
+       if (ZCG(startup_ok) &&
+           (ZCG(counted) || ZCSG(accelerator_enabled)) &&
+           !CG(interactive) &&
+           !ZCSG(restart_in_progress)) {
+
+               /* check if callback is called from include_once or it's a main request */
+               if ((!EG(opline_ptr) &&
+                    filename == SG(request_info).path_translated) ||
+                   (EG(opline_ptr) &&
+                    *EG(opline_ptr) &&
+             (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+             ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
+              (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
+#else
+              ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
+               (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
+#endif
+
+                       /* we are in include_once or FastCGI request */
+                       zend_file_handle handle;
+                       char *key = NULL;
+                       int key_length;
+                       char *resolved_path;
+                       zend_accel_hash_entry *bucket;
+                       zend_persistent_script *persistent_script;
+                   
+                   /* Check if requestd file already cached (by full name) */
+                   if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
+                        is_stream_path(filename)) &&
+                           (bucket = zend_accel_hash_find_entry(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL) {
+                               persistent_script = (zend_persistent_script *)bucket->data;
+                               if (persistent_script && !persistent_script->corrupted) {
+                                       memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1);
+                                       ZCG(key_len) = persistent_script->full_path_len;
+                                       ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
+                                       ZCG(cache_persistent_script) = persistent_script;
+                                       return estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                               }
+                   }
+
+                   /* Check if requestd file already cached (by key) */
+                       handle.filename = (char*)filename;
+                       handle.free_filename = 0;
+                       handle.opened_path = NULL;
+                       key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC);
+                       if (!ZCG(accel_directives).revalidate_path &&
+                           key &&
+                               (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) != NULL &&
+                           !persistent_script->corrupted) {
+
+                               /* we have persistent script */
+                               ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
+                               ZCG(cache_persistent_script) = persistent_script;
+                               return estrndup(persistent_script->full_path, persistent_script->full_path_len);
+                       }
+
+                       /* find the full real path */
+                       resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
+
+                   /* Check if requestd file already cached (by real path) */
+                       if (resolved_path &&
+                           (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path)+1)) != NULL) {
+                               persistent_script = (zend_persistent_script *)bucket->data;
+
+                               if (persistent_script && !persistent_script->corrupted) {
+                                       if (key && !ZCG(accel_directives).revalidate_path) {
+                                               /* add another "key" for the same bucket */
+                                       SHM_UNPROTECT();
+                                               zend_shared_alloc_lock(TSRMLS_C);
+                                               zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
+                                               zend_shared_alloc_unlock(TSRMLS_C);
+                                       SHM_PROTECT();
+                                       }
+                                       ZCG(cache_opline) = (EG(opline_ptr) && key) ? *EG(opline_ptr): NULL;
+                                       ZCG(cache_persistent_script) = key ? persistent_script : NULL;
+                                       return resolved_path;
+                               }
+                       }
+                       ZCG(cache_opline) = NULL;
+                       ZCG(cache_persistent_script) = NULL;
+                       return resolved_path;
+               }
+       }
+       ZCG(cache_opline) = NULL;
+       ZCG(cache_persistent_script) = NULL;
+       return accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
+}
+
+#endif
+
+static void zend_reset_cache_vars(TSRMLS_D)
+{
+       ZSMMG(memory_exhausted) = 0;
+       ZCSG(hits) = 0;
+       ZCSG(misses) = 0;
+       ZCSG(blacklist_misses) = 0;
+       ZSMMG(wasted_shared_memory) = 0;
+       ZCSG(restart_pending) = 0;
+       ZCSG(force_restart_time) = 0;
+}
+
+static void accel_activate(void)
+{
+       TSRMLS_FETCH();
+
+       if (!ZCG(startup_ok)) {
+               return;
+       }
+
+       SHM_UNPROTECT();
+       ZCG(request_time) = sapi_get_request_time(TSRMLS_C);
+       ZCG(cache_opline) = NULL;
+       ZCG(cache_persistent_script) = NULL;
+       ZCG(include_path_check) = !ZCG(include_path_key);
+
+       if (ZCG(counted)) {
+#ifdef ZTS
+               zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
+#else
+               zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
+#endif
+               accel_unlock_all(TSRMLS_C);
+               ZCG(counted) = 0;
+       }
+
+       if (ZCSG(restart_pending)) {
+               zend_shared_alloc_lock(TSRMLS_C);
+               if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
+                       if (accel_is_inactive(TSRMLS_C) == SUCCESS) {
+                               zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
+                               ZCSG(restart_pending) = 0;
+                               accel_restart_enter(TSRMLS_C);
+
+                               zend_reset_cache_vars(TSRMLS_C);
+                               zend_accel_hash_clean(&ZCSG(hash));
+
+                               /* include_paths keeps only the first path */
+                               if (ZCSG(include_paths).num_entries > 1) {
+                                       ZCSG(include_paths).num_entries = 1;
+                                       ZCSG(include_paths).num_direct_entries = 1;
+                                       memset(ZCSG(include_paths).hash_table, 0, sizeof(zend_accel_hash_entry*) * ZCSG(include_paths).max_num_entries);
+                                       ZCSG(include_paths).hash_table[zend_inline_hash_func(ZCSG(include_paths).hash_entries[0].key, ZCSG(include_paths).hash_entries[0].key_length) % ZCSG(include_paths).max_num_entries] = &ZCSG(include_paths).hash_entries[0];
+                               }
+
+#if (ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO) && !defined(ZTS)
+                               accel_interned_strings_restore_state(TSRMLS_C);
+#endif
+
+                               zend_shared_alloc_restore_state();
+                               ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
+                               ZCSG(last_restart_time) = ZCG(request_time);
+                               accel_restart_leave(TSRMLS_C);
+                       }
+               }
+               zend_shared_alloc_unlock(TSRMLS_C);
+       }
+
+       /* 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)));
+       }
+
+       if (ZCG(accel_directives).validate_timestamps) {
+               time_t now = ZCG(request_time);
+               if (now > ZCSG(revalidate_at) + (time_t)ZCG(accel_directives).revalidate_freq) {
+                       ZCSG(revalidate_at) = now;
+               }
+       }
+
+       ZCG(cwd) = NULL;
+
+       SHM_PROTECT();
+}
+
+#if !ZEND_DEBUG
+
+/* Fast Request Shutdown
+ * =====================
+ * Zend Memory Manager frees memory by its own. We don't have to free each
+ * allocated block separately, but we like to call all the destructors and
+ * callbacks in exactly the same order.
+ */
+
+static void accel_fast_hash_destroy(HashTable *ht)
+{
+       Bucket *p = ht->pListHead;
+
+       while (p != NULL) {
+               ht->pDestructor(p->pData);
+               p = p->pListNext;
+       }
+}
+
+static void accel_fast_zval_ptr_dtor(zval **zval_ptr)
+{
+       zval *zvalue = *zval_ptr;
+
+       if (Z_DELREF_P(zvalue)==0) {
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) {
+#else
+               switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) {
+#endif
+                       case IS_ARRAY:
+                       case IS_CONSTANT_ARRAY: {
+                                       TSRMLS_FETCH();
+
+                                       if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) {
+                                               zvalue->value.ht->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+                                               accel_fast_hash_destroy(zvalue->value.ht);
+                                       }
+                               }
+                               break;
+                       case IS_OBJECT:
+                               {
+                                       TSRMLS_FETCH();
+
+                                       Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC);
+                               }
+                               break;
+                       case IS_RESOURCE:
+                               {
+                                       TSRMLS_FETCH();
+
+                                       /* destroy resource */
+                                       zend_list_delete(zvalue->value.lval);
+                               }
+                               break;
+                       case IS_LONG:
+                       case IS_DOUBLE:
+                       case IS_BOOL:
+                       case IS_NULL:
+                       case IS_STRING:
+                       case IS_CONSTANT:
+                       default:
+                               return;
+                               break;
+               }
+       }
+}
+
+static int accel_clean_non_persistent_function(zend_function *function TSRMLS_DC)
+{
+       if (function->type == ZEND_INTERNAL_FUNCTION) {
+               return ZEND_HASH_APPLY_STOP;
+       } else {
+               if (function->op_array.static_variables) {
+                       function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+                       accel_fast_hash_destroy(function->op_array.static_variables);
+                       function->op_array.static_variables = NULL;
+               }
+               return (--(*function->op_array.refcount) <= 0) ?
+                       ZEND_HASH_APPLY_REMOVE :
+                       ZEND_HASH_APPLY_KEEP;
+       }
+}
+
+static int accel_cleanup_function_data(zend_function *function TSRMLS_DC)
+{
+       if (function->type == ZEND_USER_FUNCTION) {
+               if (function->op_array.static_variables) {
+                       function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+                       accel_fast_hash_destroy(function->op_array.static_variables);
+                       function->op_array.static_variables = NULL;
+               }
+       }
+       return 0;
+}
+
+static int accel_clean_non_persistent_class(zend_class_entry **pce TSRMLS_DC)
+{
+       zend_class_entry *ce = *pce;
+
+       if (ce->type == ZEND_INTERNAL_CLASS) {
+               return ZEND_HASH_APPLY_STOP;
+       } else {
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
+                       zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC);
+               }
+               if (ce->static_members_table) {
+                       int i;
+
+                       for (i = 0; i < ce->default_static_members_count; i++) {
+                               if (ce->static_members_table[i]) {
+                                       accel_fast_zval_ptr_dtor(&ce->static_members_table[i]);
+                                       ce->static_members_table[i] = NULL;
+                               }
+                       }
+                       ce->static_members_table = NULL;
+               }
+#else
+               zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC);
+               if (ce->static_members) {
+                       ce->static_members->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+                       accel_fast_hash_destroy(ce->static_members);
+                       ce->static_members = NULL;
+               }
+#endif
+               return ZEND_HASH_APPLY_REMOVE;
+       }
+}
+
+static int accel_clean_non_persistent_constant(zend_constant *c TSRMLS_DC)
+{
+       if (c->flags & CONST_PERSISTENT) {
+               return ZEND_HASH_APPLY_STOP;
+       } else {
+               interned_free(c->name);
+               return ZEND_HASH_APPLY_REMOVE;
+       }
+}
+
+static void zend_accel_fast_shutdown(TSRMLS_D)
+{
+       if (EG(full_tables_cleanup)) {
+               EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+       } else {
+               dtor_func_t old_destructor;
+
+               if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) {
+                       /* We don't have to destroy all zvals if they cannot call any destructors */
+
+                   old_destructor = EG(symbol_table).pDestructor;
+                       EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
+                       zend_try {
+                               zend_hash_graceful_reverse_destroy(&EG(symbol_table));
+                       } zend_end_try();
+                       EG(symbol_table).pDestructor = old_destructor;
+               }
+               zend_hash_init(&EG(symbol_table), 0, NULL, NULL, 0);
+               old_destructor = EG(function_table)->pDestructor;
+               EG(function_table)->pDestructor = NULL;
+               zend_hash_reverse_apply(EG(function_table), (apply_func_t) accel_clean_non_persistent_function TSRMLS_CC);
+               EG(function_table)->pDestructor = old_destructor;
+               old_destructor = EG(class_table)->pDestructor;
+               EG(class_table)->pDestructor = NULL;
+               zend_hash_reverse_apply(EG(class_table), (apply_func_t) accel_clean_non_persistent_class TSRMLS_CC);
+               EG(class_table)->pDestructor = old_destructor;
+               old_destructor = EG(zend_constants)->pDestructor;
+               EG(zend_constants)->pDestructor = NULL;
+               zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) accel_clean_non_persistent_constant TSRMLS_CC);
+               EG(zend_constants)->pDestructor = old_destructor;
+       }
+       CG(unclean_shutdown) = 1;
+}
+#endif
+
+static void accel_deactivate(void)
+{
+       /* ensure that we restore function_table and class_table
+        * In general, they're restored by persistent_compile_file(), but in case
+        * the script is aborted abnormally, they may become messed up.
+        */
+       TSRMLS_FETCH();
+
+       if (!ZCG(startup_ok)) {
+               return;
+       }
+
+       zend_shared_alloc_safe_unlock(TSRMLS_C); /* be sure we didn't leave cache locked */
+       accel_unlock_all(TSRMLS_C);
+       ZCG(counted) = 0;
+
+#if !ZEND_DEBUG
+       if (ZCG(accel_directives).fast_shutdown) {
+               zend_accel_fast_shutdown(TSRMLS_C);
+       }
+#endif
+
+       if (ZCG(cwd)) {
+               efree(ZCG(cwd));
+               ZCG(cwd) = NULL;
+       }
+
+}
+
+static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
+{
+       (void)element2; /* keep the compiler happy */
+
+       if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
+               element1->startup = NULL;
+#if 0
+               /* We have to call shutdown callback it to free TS resources */
+               element1->shutdown = NULL;
+#endif
+               element1->activate = NULL;
+               element1->deactivate = NULL;
+               element1->op_array_handler = NULL;
+
+#ifdef __DEBUG_MESSAGES__
+        fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
+        fflush(stderr);
+#endif
+       }
+
+       return 0;
+}
+
+static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *) TSRMLS_DC)
+{
+       ZCG(startup_ok) = 0;
+       zps_failure_reason = reason;
+       zps_api_failure_reason = api_reason?api_reason:reason;
+       zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
+}
+
+static inline int accel_find_sapi(TSRMLS_D)
+{
+       static const char *supported_sapis[] = {
+               "apache",
+               "fastcgi",
+               "cgi-fcgi",
+               "fpm-fcgi",
+               "isapi",
+               "apache2filter",
+               "apache2handler",
+               NULL
+       };
+       const char **sapi_name;
+
+       if (sapi_module.name) {
+               for (sapi_name=supported_sapis; *sapi_name; sapi_name++) {
+                       if (strcmp(sapi_module.name, *sapi_name)==0) {
+                               return SUCCESS;
+                       }
+               }
+               if (ZCG(accel_directives).enable_cli &&
+                   strcmp(sapi_module.name, "cli")==0) {
+                       return SUCCESS;
+               }
+       }
+
+       return FAILURE;
+}
+
+static void zend_accel_init_shm(TSRMLS_D)
+{
+       zend_shared_alloc_lock(TSRMLS_C);
+
+       accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
+       if (!accel_shared_globals) {
+               zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+       }
+       ZSMMG(app_shared_globals) = accel_shared_globals;
+
+       zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
+       zend_accel_hash_init(&ZCSG(include_paths), 32);
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+
+# ifndef ZTS
+       zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1);
+       ZCSG(interned_strings).nTableMask = ZCSG(interned_strings).nTableSize - 1;
+       ZCSG(interned_strings).arBuckets = zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
+       ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
+       if (!ZCSG(interned_strings).arBuckets || !ZCSG(interned_strings_start)) {
+               zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings");
+       }
+       ZCSG(interned_strings_end)   = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
+       ZCSG(interned_strings_top)   = ZCSG(interned_strings_start);
+# else
+       ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
+# endif
+
+       orig_interned_strings_start = CG(interned_strings_start);
+       orig_interned_strings_end = CG(interned_strings_end);
+       orig_new_interned_string = zend_new_interned_string;
+       orig_interned_strings_snapshot = zend_interned_strings_snapshot;
+       orig_interned_strings_restore = zend_interned_strings_restore;
+
+       CG(interned_strings_start) = ZCSG(interned_strings_start);
+       CG(interned_strings_end) = ZCSG(interned_strings_end);
+       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(TSRMLS_C);
+       accel_interned_strings_save_state(TSRMLS_C);
+# endif
+
+#endif
+
+       zend_reset_cache_vars(TSRMLS_C);
+
+       ZCSG(accelerator_enabled) = 1;
+       ZCSG(last_restart_time) = 0;
+       ZCSG(restart_in_progress) = 0;
+
+       zend_shared_alloc_unlock(TSRMLS_C);
+}
+
+static void accel_globals_ctor(zend_accel_globals *accel_globals TSRMLS_DC)
+{
+       memset(accel_globals, 0, sizeof(zend_accel_globals));
+       zend_hash_init(&accel_globals->function_table, zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
+       zend_accel_copy_internal_functions(TSRMLS_C);
+}
+
+static void accel_globals_dtor(zend_accel_globals *accel_globals TSRMLS_DC)
+{
+       accel_globals->function_table.pDestructor = NULL;
+       zend_hash_destroy(&accel_globals->function_table);
+}
+
+static int accel_startup(zend_extension *extension)
+{
+       zend_function *func;
+       zend_ini_entry *ini_entry;
+       TSRMLS_FETCH();
+
+#ifdef ZTS
+       accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor);
+#else
+       accel_globals_ctor(&accel_globals);
+#endif
+
+#ifdef ZEND_WIN32
+       _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
+#endif
+
+       if (start_accel_module(0) == FAILURE) {
+               ZCG(startup_ok) = 0;
+               zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
+               return FAILURE;
+       }
+
+       /* no supported SAPI found - disable acceleration and stop initalization */
+       if( accel_find_sapi(TSRMLS_C) == FAILURE ){
+               ZCG(startup_ok) = 0;
+               zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI and FastCGI SAPIs", NULL, accelerator_remove_cb TSRMLS_CC);
+               return SUCCESS;
+       }
+
+       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:
+                       zend_accel_init_shm(TSRMLS_C);
+                       break;
+               case ALLOC_FAILURE:
+                       ZCG(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);
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       zend_shared_alloc_lock(TSRMLS_C);
+                       orig_interned_strings_start = CG(interned_strings_start);
+                       orig_interned_strings_end = CG(interned_strings_end);
+                       orig_new_interned_string = zend_new_interned_string;
+                       orig_interned_strings_snapshot = zend_interned_strings_snapshot;
+                       orig_interned_strings_restore = zend_interned_strings_restore;
+
+                       CG(interned_strings_start) = ZCSG(interned_strings_start);
+                       CG(interned_strings_end) = ZCSG(interned_strings_end);
+                       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(TSRMLS_C);
+# endif
+
+                       zend_shared_alloc_unlock(TSRMLS_C);
+#endif
+
+                       break;
+               case FAILED_REATTACHED:
+                       ZCG(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;
+       }
+
+       /* from this point further, shared memory is supposed to be OK */
+
+       /* Override compiler */
+       accelerator_orig_compile_file = zend_compile_file;
+       zend_compile_file = persistent_compile_file;
+
+       /* Override stream opener function (to eliminate open() call caused by
+        * include/require statements ) */
+       accelerator_orig_zend_stream_open_function = zend_stream_open_function;
+       zend_stream_open_function = persistent_stream_open_function;
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       /* Override path resolver function (to eliminate stat() calls caused by
+        * include_once/require_once statements */
+       accelerator_orig_zend_resolve_path = zend_resolve_path;
+       zend_resolve_path = persistent_zend_resolve_path;
+#endif
+
+       if(ZCG(accel_directives).validate_timestamps) {
+               ZCSG(revalidate_at) = zend_accel_get_time() + ZCG(accel_directives).revalidate_freq;
+       }
+
+       /* Override chdir() function */
+       if (zend_hash_find(CG(function_table), "chdir", sizeof("chdir"), (void**)&func) == SUCCESS &&
+           func->type == ZEND_INTERNAL_FUNCTION) {
+               orig_chdir = func->internal_function.handler;
+               func->internal_function.handler = ZEND_FN(accel_chdir);
+       }
+       ZCG(cwd) = NULL;
+
+       /* Override "include_path" modifier callback */
+       if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry)==SUCCESS) {
+               ZCG(include_path) = INI_STR("include_path");
+               ZCG(include_path_key) = NULL;
+               if (ZCG(include_path) && *ZCG(include_path)) {
+                       ZCG(include_path_len) = strlen(ZCG(include_path));
+                       if (!zend_accel_hash_is_full(&ZCSG(include_paths))) {
+                               char *key;
+
+                               zend_shared_alloc_lock(TSRMLS_C);
+                               key = zend_shared_alloc(ZCG(include_path_len) + 2);
+                               if (key) {
+                                       memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
+                                       key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
+                                       ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
+                                       zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
+                               }
+                               zend_shared_alloc_unlock(TSRMLS_C);
+                       }
+               } else {
+                       ZCG(include_path) = "";
+                       ZCG(include_path_len) = 0;
+               }
+               orig_include_path_on_modify = ini_entry->on_modify;
+               ini_entry->on_modify = accel_include_path_on_modify;
+       }
+
+       zend_shared_alloc_lock(TSRMLS_C);
+       zend_shared_alloc_save_state();
+       zend_shared_alloc_unlock(TSRMLS_C);
+
+       SHM_PROTECT();
+
+       ZCG(startup_ok) = 1;
+
+       /* Override file_exists(), is_file() and is_readable() */
+       zend_accel_override_file_functions(TSRMLS_C);
+
+#if 0
+       /* FIXME: We probably don't need it here */
+       zend_accel_copy_internal_functions(TSRMLS_C);
+#endif
+
+       return SUCCESS;
+}
+
+static void accel_free_ts_resources()
+{
+#ifndef ZTS
+       accel_globals_dtor(&accel_globals);
+#else
+       ts_free_id(accel_globals_id);
+#endif
+}
+
+static void accel_shutdown(zend_extension *extension)
+{
+       zend_ini_entry *ini_entry;
+       TSRMLS_FETCH();
+
+       (void)extension; /* keep the compiler happy */
+
+       zend_accel_blacklist_shutdown(&accel_blacklist);
+
+       if (!ZCG(startup_ok)) {
+               accel_free_ts_resources();
+               return;
+       }
+
+       accel_free_ts_resources();
+       zend_shared_alloc_shutdown();
+       zend_compile_file = accelerator_orig_compile_file;
+
+       if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry)==SUCCESS) {
+               ini_entry->on_modify = orig_include_path_on_modify;
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       CG(interned_strings_start) = orig_interned_strings_start;
+       CG(interned_strings_end) = orig_interned_strings_end;
+       zend_new_interned_string = orig_new_interned_string;
+       zend_interned_strings_snapshot = orig_interned_strings_snapshot;
+       zend_interned_strings_restore = orig_interned_strings_restore;
+#endif
+
+}
+
+void zend_accel_schedule_restart(TSRMLS_D)
+{
+       if (ZCSG(restart_pending)) {
+               /* don't schedule twice */
+               return;
+       }
+       zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!");
+
+       ZCSG(restart_pending) = 1;
+       ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
+       ZCSG(accelerator_enabled) = 0;
+
+       if (ZCG(accel_directives).force_restart_timeout) {
+               ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
+       } else {
+               ZCSG(force_restart_time) = 0;
+       }
+}
+
+/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
+#ifdef ZEND_WIN32
+#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub(TSRMLS_C)
+#else
+#define accel_deactivate_now() accel_deactivate_sub(TSRMLS_C)
+#endif
+
+/* ensures it is OK to read SHM
+       if it's not OK (restart in progress) returns FAILURE
+       if OK returns SUCCESS
+       MUST call accelerator_shm_read_unlock after done lock operationss
+*/
+int accelerator_shm_read_lock(TSRMLS_D)
+{
+       if(ZCG(counted)) {
+               /* counted means we are holding read lock for SHM, so that nothing bad can happen */
+               return SUCCESS;
+       } else {
+               /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
+                       or is in progress now */
+               accel_activate_add(TSRMLS_C); /* acquire usage lock */
+               /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
+               if(ZCSG(restart_in_progress)) {
+                       /* we already were inside restart this means it's not safe to touch shm */
+                       accel_deactivate_now(); /* drop usage lock */
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+
+/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
+void accelerator_shm_read_unlock(TSRMLS_D)
+{
+       if(!ZCG(counted)) {
+               /* counted is 0 - meaning we had to readlock manually, release readlock now */
+               accel_deactivate_now();
+       }
+}
+
+static void accel_op_array_handler(zend_op_array *op_array)
+{
+       TSRMLS_FETCH();
+
+       if (ZCG(startup_ok) && ZCSG(accelerator_enabled)) {
+               zend_optimizer(op_array TSRMLS_CC);
+       }
+}
+
+ZEND_EXT_API zend_extension zend_extension_entry = {
+       ACCELERATOR_PRODUCT_NAME,               /* name */
+       ACCELERATOR_VERSION,                                    /* version */
+       "Zend Technologies",                                    /* author */
+       "http://www.zend.com/",                                 /* URL */
+       "Copyright (c) 1999-2013",                              /* copyright */
+       accel_startup,                                                  /* startup */
+       accel_shutdown,                                                 /* shutdown */
+       accel_activate,                                                 /* per-script activation */
+       accel_deactivate,                                               /* per-script deactivation */
+       NULL,                                                                   /* message handler */
+       accel_op_array_handler,                                 /* op_array handler */
+       NULL,                                                                   /* extended statement handler */
+       NULL,                                                                   /* extended fcall begin handler */
+       NULL,                                                                   /* extended fcall end handler */
+       NULL,                                                                   /* op_array ctor */
+       NULL,                                                                   /* op_array dtor */
+       STANDARD_ZEND_EXTENSION_PROPERTIES
+};
diff --git a/ZendAccelerator.h b/ZendAccelerator.h
new file mode 100644 (file)
index 0000000..68afad3
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_H
+#define ZEND_ACCELERATOR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define ACCELERATOR_PRODUCT_NAME       "Zend Optimizer+"
+#define ACCELERATOR_VERSION "7.0.0"
+/* 2 - added Profiler support, on 20010712 */
+/* 3 - added support for Optimizer's encoded-only-files mode */
+/* 4 - works with the new Optimizer, that supports the file format with licenses */
+/* 5 - API 4 didn't really work with the license-enabled file format.  v5 does. */
+/* 6 - Monitor was removed from ZendPlatform.so, to a module of its own */
+/* 7 - Optimizer was embeded into Accelerator */
+/* 8 - Standalone Open Source OptimizerPlus */
+#define ACCELERATOR_API_NO 8
+
+#if ZEND_WIN32
+# include "zend_config.w32.h"
+#else
+#include "zend_config.h"
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include "unistd.h"
+#endif
+
+#include "zend_extensions.h"
+#include "zend_compile.h"
+
+#include "Optimizer/zend_optimizer.h"
+#include "zend_accelerator_hash.h"
+#include "zend_accelerator_debug.h"
+
+#ifndef PHPAPI
+# ifdef ZEND_WIN32
+#  define PHPAPI __declspec(dllimport)
+# else
+#  define PHPAPI
+# endif
+#endif
+
+#ifndef ZEND_EXT_API
+# if WIN32|WINNT
+#  define ZEND_EXT_API __declspec(dllexport)
+# else
+#  define ZEND_EXT_API
+# endif
+#endif
+
+#ifdef ZEND_WIN32
+# ifndef MAXPATHLEN
+#  define MAXPATHLEN     _MAX_PATH
+# endif
+# include <direct.h>
+#else
+# include <sys/param.h>
+#endif
+
+#define PHP_5_0_X_API_NO               220040412
+#define PHP_5_1_X_API_NO               220051025
+#define PHP_5_2_X_API_NO               220060519
+#define PHP_5_3_X_API_NO               220090626
+#define PHP_5_4_X_API_NO               220100525
+
+/*** file locking ***/
+#ifndef ZEND_WIN32
+extern int lock_file;
+
+# if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)/* Darwin */) || defined(__OpenBSD__) || defined(__NetBSD__)
+#  define FLOCK_STRUCTURE(name, type, whence, start, len) \
+               struct flock name = {start, len, -1, type, whence}
+# elif defined(__svr4__)
+#  define FLOCK_STRUCTURE(name, type, whence, start, len) \
+               struct flock name = {type, whence, start, len}
+# elif defined(__linux__) || defined(__hpux)
+#  define FLOCK_STRUCTURE(name, type, whence, start, len) \
+               struct flock name = {type, whence, start, len, 0}
+# elif defined(_AIX)
+#  if defined(_LARGE_FILES) || defined(__64BIT__)
+#   define FLOCK_STRUCTURE(name, type, whence, start, len) \
+               struct flock name = {type, whence, 0, 0, 0, start, len }
+#  else
+#   define FLOCK_STRUCTURE(name, type, whence, start, len) \
+               struct flock name = {type, whence, start, len}
+#  endif
+# else
+#  error "Don't know how to define struct flock"
+# endif
+#endif
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       #define Z_REFCOUNT_P(pz)                                (pz)->refcount
+       #define Z_SET_REFCOUNT_P(pz, v)                 (pz)->refcount = (v)
+       #define Z_ADDREF_P(pz)                                  ++((pz)->refcount)
+       #define Z_DELREF_P(pz)                                  --((pz)->refcount)
+       #define Z_ISREF_P(pz)                                   (pz)->is_ref
+       #define Z_SET_ISREF_P(pz)                               Z_SET_ISREF_TO_P(pz, 1)
+       #define Z_UNSET_ISREF_P(pz)                             Z_SET_ISREF_TO_P(pz, 0)
+       #define Z_SET_ISREF_TO_P(pz, isref)             (pz)->is_ref = (isref)
+       #define PZ_REFCOUNT_P(pz)                               (pz)->refcount
+       #define PZ_SET_REFCOUNT_P(pz, v)                (pz)->refcount = (v)
+       #define PZ_ADDREF_P(pz)                                 ++((pz)->refcount)
+       #define PZ_DELREF_P(pz)                                 --((pz)->refcount)
+       #define PZ_ISREF_P(pz)                                  (pz)->is_ref
+       #define PZ_SET_ISREF_P(pz)                              Z_SET_ISREF_TO_P(pz, 1)
+       #define PZ_UNSET_ISREF_P(pz)                    Z_SET_ISREF_TO_P(pz, 0)
+       #define PZ_SET_ISREF_TO_P(pz, isref)    (pz)->is_ref = (isref)
+#else
+       #define PZ_REFCOUNT_P(pz)                               (pz)->refcount__gc
+       #define PZ_SET_REFCOUNT_P(pz, v)                (pz)->refcount__gc = (v)
+       #define PZ_ADDREF_P(pz)                                 ++((pz)->refcount__gc)
+       #define PZ_DELREF_P(pz)                                 --((pz)->refcount__gc)
+       #define PZ_ISREF_P(pz)                                  (pz)->is_ref__gc
+       #define PZ_SET_ISREF_P(pz)                              Z_SET_ISREF_TO_P(pz, 1)
+       #define PZ_UNSET_ISREF_P(pz)                    Z_SET_ISREF_TO_P(pz, 0)
+       #define PZ_SET_ISREF_TO_P(pz, isref)    (pz)->is_ref__gc = (isref)
+#endif
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+# ifdef ALLOCA_FLAG
+       #define DO_ALLOCA(x)    do_alloca_with_limit(x,use_heap)
+       #define FREE_ALLOCA(x)  free_alloca_with_limit(x,use_heap)
+# else
+       #define ALLOCA_FLAG(x)
+       #define DO_ALLOCA(x)    do_alloca(x)
+       #define FREE_ALLOCA(x)  free_alloca(x)
+# endif
+#else
+       #define DO_ALLOCA(x)    do_alloca(x,use_heap)
+       #define FREE_ALLOCA(x)  free_alloca(x,use_heap)
+#endif
+
+
+#if ZEND_WIN32
+typedef unsigned __int64 accel_time_t;
+#else
+typedef time_t accel_time_t;
+#endif
+
+typedef struct _zend_persistent_script {
+       ulong          hash_value;
+       char          *full_path;              /* full real path with resolved symlinks */
+       int            full_path_len;
+       zend_op_array  main_op_array;
+       HashTable      function_table;
+       HashTable      class_table;
+       long           compiler_halt_offset;   /* position of __HALT_COMPILER or -1 */
+       int            ping_auto_globals_mask; /* which autoglobals are used by the script */
+       accel_time_t   timestamp;              /* the script modification time */
+       zend_bool      corrupted;
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       zend_uint      early_binding;          /* the linked list of delayed declarations */
+#endif
+
+       void          *mem;                    /* shared memory area used by script structures */
+       size_t         size;                   /* size of used shared memory */
+
+       /* All entries that shouldn't be counted in the ADLER32
+        * checksum must be declared in this struct
+        */
+       struct zend_persistent_script_dynamic_members {
+               time_t       last_used;
+               ulong        hits;
+               unsigned int memory_consumption;
+               unsigned int checksum;
+               time_t       revalidate;
+       } dynamic_members;
+} zend_persistent_script;
+
+typedef struct _zend_accel_directives {
+       long           memory_consumption;
+       long           max_accelerated_files;
+       double         max_wasted_percentage;
+       char          *user_blacklist_filename;
+       long           consistency_checks;
+       long           force_restart_timeout;
+       zend_bool      use_cwd;
+       zend_bool      ignore_dups;
+       zend_bool      validate_timestamps;
+       zend_bool      revalidate_path;
+       zend_bool      save_comments;
+       zend_bool      fast_shutdown;
+       zend_bool      protect_memory;
+       zend_bool      file_override_enabled;
+       zend_bool      inherited_hack;
+       zend_bool      enable_cli;
+       unsigned long  revalidate_freq;
+       char          *error_log;
+#ifdef ZEND_WIN32
+       char          *mmap_base;
+#endif
+       char          *memory_model;
+       long           log_verbosity_level;
+
+       long           optimization_level;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       long           interned_strings_buffer;
+#endif
+} zend_accel_directives;
+
+typedef struct _zend_accel_globals {
+    /* copy of CG(function_table) used for compilation scripts into cashe */
+    /* imitially it contains only internal functions */
+       HashTable               function_table;
+       int                     internal_functions_count;
+       int                     counted;   /* the process uses shatred memory */
+       zend_bool               enabled;
+       HashTable               bind_hash; /* prototype and zval lookup table */
+       zend_accel_directives   accel_directives;
+       char                   *cwd;              /* current working directory or NULL */
+       int                     cwd_len;          /* "cwd" string lenght */
+       char                   *include_path_key; /* one letter key of current "include_path" */
+       char                   *include_path;     /* current settion of "include_path" directive */
+       int                     include_path_len; /* "include_path" string lenght */
+       int                     include_path_check;
+       time_t                  request_time;
+       zend_bool               startup_ok;
+       /* preallocated shared-memory block to save current script */
+       void                   *mem;
+       /* cache to save hash lookup on the same INCLUDE opcode */
+       zend_op                *cache_opline;
+       zend_persistent_script *cache_persistent_script;
+       /* preallocated buffer for keys */
+       int                     key_len;
+       char                    key[MAXPATHLEN * 8];
+} zend_accel_globals;
+
+typedef struct _zend_accel_shared_globals {
+       /* Cache Data Structures */
+       unsigned long   hits;
+       unsigned long   misses;
+       unsigned long   blacklist_misses;
+       zend_accel_hash hash;             /* hash table for cached scripts */
+       zend_accel_hash include_paths;    /* used "include_path" values    */
+
+       /* Directives & Maintenance */
+       time_t          last_restart_time;
+       time_t          force_restart_time;
+       zend_bool       accelerator_enabled;
+       zend_bool       restart_pending;
+       zend_bool       cache_status_before_restart;
+#ifdef ZEND_WIN32
+    unsigned long   mem_usage;
+    unsigned long   restart_in;
+#endif
+       zend_bool       restart_in_progress;
+    time_t          revalidate_at;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       /* Interned Strings Support */
+       char           *interned_strings_start;
+       char           *interned_strings_top;
+       char           *interned_strings_end;
+       HashTable       interned_strings;
+       struct {
+               Bucket **arBuckets;
+               Bucket  *pListHead;
+               Bucket  *pListTail;
+               char    *top;
+       } interned_strings_saved_state;
+#endif
+} zend_accel_shared_globals;
+
+extern zend_accel_shared_globals *accel_shared_globals;
+#define ZCSG(element)   (accel_shared_globals->element)
+
+#ifdef ZTS
+# define ZCG(v)        TSRMG(accel_globals_id, zend_accel_globals *, v)
+extern int accel_globals_id;
+#else
+# define ZCG(v) (accel_globals.v)
+extern zend_accel_globals accel_globals;
+#endif
+
+extern char *zps_api_failure_reason;
+
+void zend_accel_schedule_restart(TSRMLS_D);
+int  accelerator_shm_read_lock(TSRMLS_D);
+void accelerator_shm_read_unlock(TSRMLS_D);
+
+char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC);
+
+#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED)
+# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145
+#endif
+
+#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+
+const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC);
+
+# define interned_free(s) do { \
+               if (!IS_INTERNED(s)) { \
+                       free(s); \
+               } \
+       } while (0)
+# define interned_efree(s) do { \
+               if (!IS_INTERNED(s)) { \
+                       efree(s); \
+               } \
+       } while (0)
+# define interned_estrndup(s, n) \
+       (IS_INTERNED(s) ? (s) : estrndup(s, n))
+# define ZEND_RESULT_TYPE(opline)      (opline)->result_type
+# define ZEND_RESULT(opline)           (opline)->result
+# define ZEND_OP1_TYPE(opline)         (opline)->op1_type
+# define ZEND_OP1(opline)                      (opline)->op1
+# define ZEND_OP1_CONST(opline)                (*(opline)->op1.zv)
+# define ZEND_OP1_LITERAL(opline)      (op_array)->literals[(opline)->op1.constant].constant
+# define ZEND_OP2_TYPE(opline)         (opline)->op2_type
+# define ZEND_OP2(opline)                      (opline)->op2
+# define ZEND_OP2_CONST(opline)                (*(opline)->op2.zv)
+# define ZEND_OP2_LITERAL(opline)      (op_array)->literals[(opline)->op2.constant].constant
+# define ZEND_DONE_PASS_TWO(op_array)  (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0)
+# define ZEND_CE_FILENAME(ce)                  (ce)->info.user.filename
+# define ZEND_CE_DOC_COMMENT(ce)        (ce)->info.user.doc_comment
+# define ZEND_CE_DOC_COMMENT_LEN(ce)   (ce)->info.user.doc_comment_len
+#else
+# define IS_INTERNED(s)                                0
+# define interned_free(s)                      free(s)
+# define interned_efree(s)                     efree(s)
+# define interned_estrndup(s, n)       estrndup(s, n)
+# define ZEND_RESULT_TYPE(opline)      (opline)->result.op_type
+# define ZEND_RESULT(opline)           (opline)->result.u
+# define ZEND_OP1_TYPE(opline)         (opline)->op1.op_type
+# define ZEND_OP1(opline)                      (opline)->op1.u
+# define ZEND_OP1_CONST(opline)                (opline)->op1.u.constant
+# define ZEND_OP1_LITERAL(opline)      (opline)->op1.u.constant
+# define ZEND_OP2_TYPE(opline)         (opline)->op2.op_type
+# define ZEND_OP2(opline)                      (opline)->op2.u
+# define ZEND_OP2_CONST(opline)                (opline)->op2.u.constant
+# define ZEND_OP2_LITERAL(opline)      (opline)->op2.u.constant
+# define ZEND_DONE_PASS_TWO(op_array)  ((op_array)->done_pass_two != 0)
+# define ZEND_CE_FILENAME(ce)                  (ce)->filename
+# define ZEND_CE_DOC_COMMENT(ce)        (ce)->doc_comment
+# define ZEND_CE_DOC_COMMENT_LEN(ce)   (ce)->doc_comment_len
+#endif
+
+#endif /* ZEND_ACCELERATOR_H */
diff --git a/config.m4 b/config.m4
new file mode 100644 (file)
index 0000000..6e178c1
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,345 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(optimizer-plus, whether to enable Zend OptimizerPlus support,
+[  --enable-optimizer-plus Enable Zend OptimizerPlus support])
+
+if test "$PHP_OPTIMIZER_PLUS" != "no"; then
+  AC_DEFINE(HAVE_OPTIMIZER_PLUS, 1, [ ])
+
+  AC_CHECK_FUNC(mprotect,[
+    AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function])
+  ])
+
+  AC_MSG_CHECKING(for sysvipc shared memory support)
+  AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <unistd.h>
+#include <string.h>
+
+int main() {
+  pid_t pid;
+  int status;
+  int ipc_id;
+  char *shm;
+  struct shmid_ds shmbuf;
+
+  ipc_id = shmget(IPC_PRIVATE, 4096, (IPC_CREAT | SHM_R | SHM_W));
+  if (ipc_id == -1) {
+    return 1;
+  }
+
+  shm = shmat(ipc_id, NULL, 0);
+  if (shm == (void *)-1) {
+    shmctl(ipc_id, IPC_RMID, NULL);
+    return 2;
+  }
+
+  if (shmctl(ipc_id, IPC_STAT, &shmbuf) != 0) {
+    shmdt(shm);
+    shmctl(ipc_id, IPC_RMID, NULL);
+    return 3;
+  }
+
+  shmbuf.shm_perm.uid = getuid();
+  shmbuf.shm_perm.gid = getgid();
+  shmbuf.shm_perm.mode = 0600;
+
+  if (shmctl(ipc_id, IPC_SET, &shmbuf) != 0) {
+    shmdt(shm);
+    shmctl(ipc_id, IPC_RMID, NULL);
+    return 4;
+  }
+
+  shmctl(ipc_id, IPC_RMID, NULL);
+
+  strcpy(shm, "hello");
+
+  pid = fork();
+  if (pid < 0) {
+    return 5;
+  } else if (pid == 0) {
+    strcpy(shm, "bye");
+    return 6;
+  }
+  if (wait(&status) != pid) {
+    return 7;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
+    return 8;
+  }
+  if (strcmp(shm, "bye") != 0) {
+    return 9;
+  }
+  return 0;
+}
+],dnl
+    AC_DEFINE(HAVE_SHM_IPC, 1, [Define if you have SysV IPC SHM support])
+    msg=yes,msg=no,msg=no)
+  AC_MSG_RESULT([$msg])
+
+  AC_MSG_CHECKING(for mmap() using MAP_ANON shared memory support)
+  AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifndef MAP_ANON
+# ifdef MAP_ANONYMOUS
+#  define MAP_ANON MAP_ANONYMOUS
+# endif
+#endif
+#ifndef MAP_FAILED
+# define MAP_FAILED ((void*)-1)
+#endif
+
+int main() {
+  pid_t pid;
+  int status;
+  char *shm;
+
+  shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+  if (shm == MAP_FAILED) {
+    return 1;
+  }
+
+  strcpy(shm, "hello");
+
+  pid = fork();
+  if (pid < 0) {
+    return 5;
+  } else if (pid == 0) {
+    strcpy(shm, "bye");
+    return 6;
+  }
+  if (wait(&status) != pid) {
+    return 7;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
+    return 8;
+  }
+  if (strcmp(shm, "bye") != 0) {
+    return 9;
+  }
+  return 0;
+}
+],dnl
+    AC_DEFINE(HAVE_SHM_MMAP_ANON, 1, [Define if you have mmap(MAP_ANON) SHM support])
+    msg=yes,msg=no,msg=no)
+  AC_MSG_RESULT([$msg])
+
+  AC_MSG_CHECKING(for mmap() using /dev/zero shared memory support)
+  AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifndef MAP_FAILED
+# define MAP_FAILED ((void*)-1)
+#endif
+
+int main() {
+  pid_t pid;
+  int status;
+  int fd;
+  char *shm;
+
+  fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
+  if (fd == -1) {
+    return 1;
+  }
+
+  shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (shm == MAP_FAILED) {
+    return 2;
+  }
+
+  strcpy(shm, "hello");
+
+  pid = fork();
+  if (pid < 0) {
+    return 5;
+  } else if (pid == 0) {
+    strcpy(shm, "bye");
+    return 6;
+  }
+  if (wait(&status) != pid) {
+    return 7;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
+    return 8;
+  }
+  if (strcmp(shm, "bye") != 0) {
+    return 9;
+  }
+  return 0;
+}
+],dnl
+    AC_DEFINE(HAVE_SHM_MMAP_ZERO, 1, [Define if you have mmap("/dev/zero") SHM support])
+    msg=yes,msg=no,msg=no)
+  AC_MSG_RESULT([$msg])
+
+  AC_MSG_CHECKING(for mmap() using shm_open() shared memory support)
+  AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef MAP_FAILED
+# define MAP_FAILED ((void*)-1)
+#endif
+
+int main() {
+  pid_t pid;
+  int status;
+  int fd;
+  char *shm;
+  char tmpname[4096];
+
+  sprintf(tmpname,"test.shm.%dXXXXXX", getpid());
+  if (mktemp(tmpname) == NULL) {
+    return 1;
+  }
+  fd = shm_open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+  if (fd == -1) {
+    return 2;
+  }
+  if (ftruncate(fd, 4096) < 0) {
+    close(fd);
+    shm_unlink(tmpname);
+    return 3;
+  }
+
+  shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (shm == MAP_FAILED) {
+    return 4;
+  }
+  shm_unlink(tmpname);
+  close(fd);
+
+  strcpy(shm, "hello");
+
+  pid = fork();
+  if (pid < 0) {
+    return 5;
+  } else if (pid == 0) {
+    strcpy(shm, "bye");
+    return 6;
+  }
+  if (wait(&status) != pid) {
+    return 7;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
+    return 8;
+  }
+  if (strcmp(shm, "bye") != 0) {
+    return 9;
+  }
+  return 0;
+}
+],dnl
+    AC_DEFINE(HAVE_SHM_MMAP_POSIX, 1, [Define if you have POSIX mmap() SHM support])
+    msg=yes,msg=no,msg=no)
+  AC_MSG_RESULT([$msg])
+
+  AC_MSG_CHECKING(for mmap() using regular file shared memory support)
+  AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef MAP_FAILED
+# define MAP_FAILED ((void*)-1)
+#endif
+
+int main() {
+  pid_t pid;
+  int status;
+  int fd;
+  char *shm;
+  char tmpname[4096];
+
+  sprintf(tmpname,"test.shm.%dXXXXXX", getpid());
+  if (mktemp(tmpname) == NULL) {
+    return 1;
+  }
+  fd = open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+  if (fd == -1) {
+    return 2;
+  }
+  if (ftruncate(fd, 4096) < 0) {
+    close(fd);
+    unlink(tmpname);
+    return 3;
+  }
+
+  shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (shm == MAP_FAILED) {
+    return 4;
+  }
+  unlink(tmpname);
+  close(fd);
+
+  strcpy(shm, "hello");
+
+  pid = fork();
+  if (pid < 0) {
+    return 5;
+  } else if (pid == 0) {
+    strcpy(shm, "bye");
+    return 6;
+  }
+  if (wait(&status) != pid) {
+    return 7;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
+    return 8;
+  }
+  if (strcmp(shm, "bye") != 0) {
+    return 9;
+  }
+  return 0;
+}
+],dnl
+    AC_DEFINE(HAVE_SHM_MMAP_FILE, 1, [Define if you have mmap() SHM support])
+    msg=yes,msg=no,msg=no)
+  AC_MSG_RESULT([$msg])
+  
+  PHP_NEW_EXTENSION(ZendOptimizerPlus,
+       ZendAccelerator.c \
+       zend_accelerator_blacklist.c \
+       zend_accelerator_debug.c \
+       zend_accelerator_hash.c \
+       zend_accelerator_module.c \
+       zend_persist.c \
+       zend_persist_calc.c \
+       zend_shared_alloc.c \
+       zend_accelerator_util_funcs.c \
+       shared_alloc_shm.c \
+       shared_alloc_mmap.c \
+       shared_alloc_posix.c \
+       Optimizer/zend_optimizer.c,
+       $ext_shared)
+fi
diff --git a/config.w32 b/config.w32
new file mode 100644 (file)
index 0000000..6f3c89c
--- /dev/null
@@ -0,0 +1,28 @@
+ARG_ENABLE("optimizer-plus", "whether to enable Zend OptimizerPlus support", "yes");
+
+if (PHP_OPTIMIZER_PLUS != "no") {
+
+  PHP_PGI = "no"; // workaround
+  PHP_PGO = "no"; // workaround
+
+  EXTENSION('ZendOptimizerPlus', "\
+    ZendAccelerator.c \
+       zend_accelerator_blacklist.c \
+       zend_accelerator_debug.c \
+       zend_accelerator_hash.c \
+       zend_accelerator_module.c \
+       zend_accelerator_util_funcs.c \
+       zend_persist.c \
+       zend_persist_calc.c \
+       zend_shared_alloc.c \
+       shared_alloc_win32.c", true);
+
+  ADD_SOURCES("Optimizer", "zend_optimizer.c", "ZendOptimizerPlus", "OptimizerObj");
+
+  
+  ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/I .");
+  ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/D HAVE_OPTIMIZER_PLUS=1");
+
+  ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/Dregexec=php_regexec /Dregerror=php_regerror /Dregfree=php_regfree /Dregcomp=php_regcomp /Iext/ereg/regex");
+
+}
diff --git a/shared_alloc_mmap.c b/shared_alloc_mmap.c
new file mode 100644 (file)
index 0000000..7e23d4e
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "zend_shared_alloc.h"
+
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+       zend_shared_segment *shared_segment;
+
+       *shared_segments_count = 1;
+       *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
+       shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
+       (*shared_segments_p)[0] = shared_segment;
+
+       shared_segment->p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+       if(shared_segment->p == MAP_FAILED) {
+               *error_in = "mmap";
+               return ALLOC_FAILURE;
+       }
+
+       shared_segment->pos = 0;
+       shared_segment->size = requested_size;
+
+       return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment *shared_segment)
+{
+       munmap(shared_segment->p, shared_segment->size);
+       return 0;
+}
+
+static size_t segment_type_size(void)
+{
+       return sizeof(zend_shared_segment);
+}
+
+zend_shared_memory_handlers zend_alloc_mmap_handlers = {
+       create_segments,
+       detach_segment,
+       segment_type_size
+};
diff --git a/shared_alloc_posix.c b/shared_alloc_posix.c
new file mode 100644 (file)
index 0000000..5a0111c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "zend_shared_alloc.h"
+
+typedef struct  {
+    zend_shared_segment common;
+    int shm_fd;
+} zend_shared_segment_posix;
+
+static int create_segments(size_t requested_size, zend_shared_segment_posix ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+       zend_shared_segment_posix *shared_segment;
+       char shared_segment_name[sizeof("/ZendAccelerator.")+20];
+
+       *shared_segments_count = 1;
+       *shared_segments_p = (zend_shared_segment_posix **) calloc(1, sizeof(zend_shared_segment_posix)+sizeof(void *));
+       shared_segment = (zend_shared_segment_posix *)((char *)(*shared_segments_p) + sizeof(void *));
+       (*shared_segments_p)[0] = shared_segment;
+
+       sprintf(shared_segment_name, "/ZendAccelerator.%d", getpid());
+       shared_segment->shm_fd = shm_open(shared_segment_name, O_RDWR|O_CREAT|O_TRUNC, 0600);
+       if(shared_segment->shm_fd == -1) {
+               *error_in = "shm_open";
+               return ALLOC_FAILURE;
+       }
+
+       if(ftruncate(shared_segment->shm_fd, requested_size) != 0) {
+               *error_in = "ftruncate";
+               shm_unlink(shared_segment_name);
+               return ALLOC_FAILURE;
+       }
+
+       shared_segment->common.p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_segment->shm_fd, 0);
+       if(shared_segment->common.p == MAP_FAILED) {
+               *error_in = "mmap";
+               shm_unlink(shared_segment_name);
+               return ALLOC_FAILURE;
+       }
+       shm_unlink(shared_segment_name);
+
+       shared_segment->common.pos = 0;
+       shared_segment->common.size = requested_size;
+
+       return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment_posix *shared_segment)
+{
+       munmap(shared_segment->common.p, shared_segment->common.size);
+       close(shared_segment->shm_fd);
+       return 0;
+}
+
+static size_t segment_type_size(void)
+{
+       return sizeof(zend_shared_segment_posix);
+}
+
+zend_shared_memory_handlers zend_alloc_posix_handlers = {
+       (create_segments_t)create_segments,
+       (detach_segment_t)detach_segment,
+       segment_type_size
+};
diff --git a/shared_alloc_shm.c b/shared_alloc_shm.c
new file mode 100644 (file)
index 0000000..45fab7c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#if defined(__FreeBSD__)
+# include <machine/param.h>
+#endif
+#include <sys/types.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+#include <dirent.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "zend_shared_alloc.h"
+
+#ifndef MIN
+# define MIN(x, y) ((x) > (y)? (y) : (x))
+#endif
+
+#define SEG_ALLOC_SIZE_MAX 32*1024*1024
+#define SEG_ALLOC_SIZE_MIN 2*1024*1024
+
+typedef struct  {
+    zend_shared_segment common;
+    int shm_id;
+} zend_shared_segment_shm;
+
+static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+       int i;
+       unsigned int allocate_size=0, remaining_bytes=requested_size, seg_allocate_size;
+       int first_segment_id=-1;
+       key_t first_segment_key = -1;
+       struct shmid_ds sds;
+       int shmget_flags;
+       zend_shared_segment_shm *shared_segments;
+
+    seg_allocate_size = SEG_ALLOC_SIZE_MAX;
+    /* determine segment size we _really_ need:
+     * no more than to include requested_size
+     */
+    while (requested_size*2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) {
+        seg_allocate_size >>= 1;
+    }
+
+       shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL;
+
+       /* try allocating this much, if not - try shrinking */
+       while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
+               allocate_size = MIN(requested_size, seg_allocate_size);
+               first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags);
+               if (first_segment_id != -1) {
+                       break;
+               }
+               seg_allocate_size >>= 1; /* shrink the allocated block */
+       }
+
+       if (first_segment_id == -1) {
+               *error_in = "shmget";
+               return ALLOC_FAILURE;
+       }
+
+       *shared_segments_count = ((requested_size-1)/seg_allocate_size) + 1;
+       *shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count)*sizeof(zend_shared_segment_shm)+sizeof(void *)*(*shared_segments_count));
+       shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *)*(*shared_segments_count));
+       for(i=0; i<*shared_segments_count; i++) {
+               (*shared_segments_p)[i] = shared_segments+i;
+       }
+
+       remaining_bytes = requested_size;
+       for (i=0; i<*shared_segments_count; i++) {
+               allocate_size = MIN(remaining_bytes, seg_allocate_size);
+               if (i != 0) {
+                       shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags);
+               } else {
+                       shared_segments[i].shm_id = first_segment_id;
+               }
+
+               if (shared_segments[i].shm_id==-1) {
+                       return ALLOC_FAILURE;
+               }
+
+               shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0);
+               if (((int) shared_segments[i].common.p) == -1) {
+                       *error_in = "shmat";
+                       shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
+                       return ALLOC_FAILURE;
+               }
+               shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
+
+               shared_segments[i].common.pos = 0;
+               shared_segments[i].common.size = allocate_size;
+               remaining_bytes -= allocate_size;
+       }
+       return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment_shm *shared_segment)
+{
+       shmdt(shared_segment->common.p);
+       return 0;
+}
+
+static size_t segment_type_size(void)
+{
+       return sizeof(zend_shared_segment_shm);
+}
+
+zend_shared_memory_handlers zend_alloc_shm_handlers = {
+       (create_segments_t)create_segments,
+       (detach_segment_t)detach_segment,
+       segment_type_size
+};
diff --git a/shared_alloc_win32.c b/shared_alloc_win32.c
new file mode 100644 (file)
index 0000000..5388ce1
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "ZendAccelerator.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_util_funcs.h"
+#include <winbase.h>
+#include <process.h>
+#include <LMCONS.H>
+
+#define ACCEL_FILEMAP_NAME "ZendOptimizer+.SharedMemoryArea"
+#define ACCEL_MUTEX_NAME "ZendOptimizer+.SharedMemoryMutex"
+#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
+#define ACCEL_FILEMAP_BASE "ZendOptimizer+.MemoryBase"
+#define ACCEL_EVENT_SOURCE "Zend Optimizer+"
+
+static HANDLE memfile = NULL, memory_mutex = NULL;
+static void *mapping_base;
+
+#define MAX_MAP_RETRIES 25
+
+static void zend_win_error_message(int type, char *msg, int err)
+{
+       LPVOID lpMsgBuf;
+       FILE *fp;
+       HANDLE h;
+       char *ev_msgs[2];
+
+       FormatMessage(
+               FORMAT_MESSAGE_ALLOCATE_BUFFER |
+               FORMAT_MESSAGE_FROM_SYSTEM |
+               FORMAT_MESSAGE_IGNORE_INSERTS,
+               NULL,
+               err,
+               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+               (LPTSTR) &lpMsgBuf,
+               0,
+               NULL
+       );
+
+       h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE));
+       ev_msgs[0] = msg;
+       ev_msgs[1] = lpMsgBuf;
+       ReportEvent(h,                            // event log handle
+            EVENTLOG_ERROR_TYPE,  // event type
+            0,                    // category zero
+            err,                                 // event identifier
+            NULL,                 // no user security identifier
+            2,                    // one substitution string
+            0,                    // no data
+            ev_msgs,              // pointer to string array
+            NULL);                // pointer to data
+       DeregisterEventSource(h);
+
+       LocalFree( lpMsgBuf );
+
+       zend_accel_error(type, msg);
+}
+
+static char *create_name_with_username(char *name)
+{
+       static char newname[MAXPATHLEN+UNLEN+4];
+       char uname[UNLEN+1];
+       DWORD unsize = UNLEN;
+
+       GetUserName(uname, &unsize);
+       snprintf(newname, sizeof(newname)-1, "%s@%s", name, uname);
+       return newname;
+}
+
+static char *get_mmap_base_file()
+{
+       static char windir[MAXPATHLEN+UNLEN+3+sizeof("\\\\@")];
+       char uname[UNLEN+1];
+       DWORD unsize = UNLEN;
+       int l;
+
+       GetTempPath(MAXPATHLEN, windir);
+       GetUserName(uname, &unsize);
+       l = strlen(windir);
+       snprintf(windir+l, sizeof(windir)-l-1, "\\%s@%s", ACCEL_FILEMAP_BASE, uname);
+       return windir;
+}
+
+void zend_shared_alloc_create_lock(void)
+{
+       memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME));
+       ReleaseMutex(memory_mutex);
+}
+
+void zend_shared_alloc_lock_win32()
+{
+       DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE);
+
+       if(waitRes == WAIT_FAILED) {
+               zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex");
+       }
+}
+
+void zend_shared_alloc_unlock_win32(TSRMLS_D)
+{
+       ReleaseMutex(memory_mutex);
+}
+
+static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
+{
+       int err;
+       void *wanted_mapping_base;
+       char *mmap_base_file = get_mmap_base_file();
+       FILE *fp = fopen(mmap_base_file, "r");
+       MEMORY_BASIC_INFORMATION info;
+
+       err = GetLastError();
+       if(!fp) {
+               zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err);
+               *error_in="fopen";
+               return ALLOC_FAILURE;
+       }
+       if(!fscanf(fp, "%p", &wanted_mapping_base)) {
+               err = GetLastError();
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err);
+               *error_in="read mapping base";
+               return ALLOC_FAILURE;
+       }
+       fclose(fp);
+
+       /* Check if the requested address space is free */
+       if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
+           info.State != MEM_FREE ||
+           info.RegionSize < requested_size) {
+           err = ERROR_INVALID_ADDRESS;
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
+               return ALLOC_FAILURE;
+       }
+
+       mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
+       err = GetLastError();
+
+       if(mapping_base == NULL) {
+               if (err == ERROR_INVALID_ADDRESS) {
+                       zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
+                       return ALLOC_FAILURE;
+               }
+               return ALLOC_FAIL_MAPPING;
+       }
+       smm_shared_globals = (zend_smm_shared_globals *) (((char *) mapping_base) + sizeof(zend_shared_memory_block_header));
+
+       return SUCCESSFULLY_REATTACHED;
+}
+
+static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+       int err, ret;
+       zend_shared_segment *shared_segment;
+       int map_retries = 0;
+       void *default_mapping_base_set[] = { 0, 0 };
+       void *vista_mapping_base_set[] = { (void *)0x20000000, (void *)0x21000000, (void *)0x30000000, (void *)0x31000000, (void *)0x50000000, 0 };
+       void **wanted_mapping_base = default_mapping_base_set;
+       TSRMLS_FETCH();
+
+       /* Mapping retries: When Apache2 restarts, the parent process startup routine
+          can be called before the child process is killed. In this case, the map will fail
+          and we have to sleep some time (until the child releases the mapping object) and retry.*/
+       do {
+               memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
+               err = GetLastError();
+               if (memfile == NULL)
+                       break;
+
+               ret =  zend_shared_alloc_reattach(requested_size, error_in);
+               err = GetLastError();
+               if (ret == ALLOC_FAIL_MAPPING) {
+                       /* Mapping failed, wait for mapping object to get freed and retry */
+            CloseHandle(memfile);
+                       memfile = NULL;
+                       Sleep(1000*(map_retries+1));
+               } else {
+                       return ret;
+               }
+       } while(++map_retries < MAX_MAP_RETRIES);
+
+       if(map_retries == MAX_MAP_RETRIES) {
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err);
+               *error_in = "OpenFileMapping";
+               return ALLOC_FAILURE;
+       }
+
+       /* creating segment here */
+       *shared_segments_count = 1;
+       *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
+       shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
+       (*shared_segments_p)[0] = shared_segment;
+
+       memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size,
+                                                               create_name_with_username(ACCEL_FILEMAP_NAME));
+       err = GetLastError();
+       if(memfile == NULL) {
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err);
+               *error_in = "CreateFileMapping";
+               return ALLOC_FAILURE;
+       }
+
+       /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to
+          be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses
+          in high memory. */
+       if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
+               do {
+                       OSVERSIONINFOEX osvi;
+                       SYSTEM_INFO si;
+
+                       ZeroMemory(&si, sizeof(SYSTEM_INFO));
+                       ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+
+                       osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+                       if (! GetVersionEx ((OSVERSIONINFO *) &osvi)) {
+                               osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+                               if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
+                                       break;
+                       }
+
+                       GetSystemInfo(&si);
+
+                       /* Are we running Vista ? */
+                       if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 6 ) {
+                               /* Assert that platform is 32 bit (for 64 bit we need to test a different set */
+                               if(si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL)
+                                       DebugBreak();
+
+                               wanted_mapping_base = vista_mapping_base_set;
+                       }
+               } while (0);
+       } else {
+               char *s = ZCG(accel_directives).mmap_base;
+
+               /* skip leading 0x, %p assumes hexdeciaml format anyway */
+               if (*s == '0' && *(s+1) == 'x') {
+                       s += 2;
+               }
+               if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) {
+                       zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in zend_optimizerplus.mmap_base", err);
+                       return ALLOC_FAILURE;
+               }
+       }
+
+       do {
+               shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base);
+               if(*wanted_mapping_base == NULL) /* Auto address (NULL) is the last option on the array */
+                       break;
+               wanted_mapping_base++;
+       } while (!mapping_base);
+
+       err = GetLastError();
+       if(mapping_base == NULL) {
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err);
+               *error_in = "MapViewOfFile";
+               return ALLOC_FAILURE;
+       } else {
+               char *mmap_base_file = get_mmap_base_file();
+               FILE *fp = fopen(mmap_base_file, "w");
+               err = GetLastError();
+               if(!fp) {
+                       zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
+                       zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err);
+               }
+               fprintf(fp, "%p\n", mapping_base);
+               fclose(fp);
+       }
+
+       shared_segment->pos = 0;
+       shared_segment->size = requested_size;
+
+       return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment *shared_segment)
+{
+       if(mapping_base) {
+               UnmapViewOfFile(mapping_base);
+       }
+       CloseHandle(memfile);
+       ReleaseMutex(memory_mutex);
+       CloseHandle(memory_mutex);
+       return 0;
+}
+
+static size_t segment_type_size(void)
+{
+       return sizeof(zend_shared_segment);
+}
+
+zend_shared_memory_handlers zend_alloc_win32_handlers = {
+       create_segments,
+       detach_segment,
+       segment_type_size
+};
diff --git a/zend_accelerator_blacklist.c b/zend_accelerator_blacklist.c
new file mode 100644 (file)
index 0000000..7ceadea
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "main/php.h"
+#include "main/fopen_wrappers.h"
+#include "ZendAccelerator.h"
+#include "zend_accelerator_blacklist.h"
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+# include "ext/ereg/php_regex.h"
+#else
+# include "main/php_regex.h"
+#endif
+
+#ifdef ZEND_WIN32
+# define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE)
+#else
+# define REGEX_MODE (REG_EXTENDED|REG_NOSUB)
+#endif
+
+#define ZEND_BLACKLIST_BLOCK_SIZE      32
+
+struct _zend_regexp_list {
+       regex_t           comp_regex;
+       zend_regexp_list *next;
+};
+
+zend_blacklist accel_blacklist;
+
+void zend_accel_blacklist_init(zend_blacklist *blacklist)
+{
+       blacklist->pos = 0;
+       blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE;
+
+       if( blacklist->entries != NULL ){
+               zend_accel_blacklist_shutdown(blacklist);
+       }
+
+       blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
+       blacklist->regexp_list = NULL;
+}
+
+static void blacklist_report_regexp_error(regex_t *comp_regex, int reg_err)
+{
+       char *errbuf;
+       int errsize = regerror(reg_err, comp_regex, NULL, 0);
+       errbuf = malloc(errsize);
+
+       regerror(reg_err, comp_regex, errbuf, errsize);
+       zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: %s\n", errbuf);
+       free(errbuf);
+}
+
+static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
+{
+       int i, end=0, j, rlen=6, clen, reg_err;
+       char *regexp;
+       zend_regexp_list **regexp_list_it;
+
+       if (blacklist->pos == 0) {
+               /* we have no blacklist to talk about */
+               return;
+       }
+
+       regexp_list_it = &(blacklist->regexp_list);
+       for (i=0; i<blacklist->pos; i++) {
+               rlen += blacklist->entries[i].path_length*2+2;
+
+               /* don't create a regexp buffer bigger than 12K)*/
+               if((i+1 == blacklist->pos) || ((rlen+blacklist->entries[i+1].path_length*2+2)>(12*1024) ) ) {
+                       regexp = (char *)malloc(rlen);
+                       regexp[0] = '^';
+                       regexp[1] = '(';
+
+                       clen=2;
+                       for (j=end; j<=i ;j++) {
+
+                               int c;
+                               if (j!=end) {
+                                       regexp[clen++] = '|';
+                               }
+                               /* copy mangled filename */
+                               for(c=0; c<blacklist->entries[j].path_length; c++) {
+                                       if(strchr("^.[]$()|*+?{}\\", blacklist->entries[j].path[c])) {
+                                               regexp[clen++] = '\\';
+                                       }
+                                       regexp[clen++] = blacklist->entries[j].path[c];
+                               }
+                       }
+                       regexp[clen++] = ')';
+                       regexp[clen] = '\0';
+
+                       (*regexp_list_it) = malloc(sizeof(zend_regexp_list));
+                       (*regexp_list_it)->next = NULL;
+
+                       if ((reg_err = regcomp(&((*regexp_list_it)->comp_regex), regexp, REGEX_MODE)) != 0) {
+                               blacklist_report_regexp_error(&((*regexp_list_it)->comp_regex), reg_err);
+                       }
+                       /* prepare for the next iteration */
+                       free(regexp);
+                       end = i+1;
+                       rlen = 6;
+                       regexp_list_it = &((*regexp_list_it)->next);
+               }
+       }
+}
+
+void zend_accel_blacklist_shutdown(zend_blacklist *blacklist)
+{
+       zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos;
+
+       while (p<end) {
+               free(p->path);
+               p++;
+       }
+       free(blacklist->entries);
+       blacklist->entries = NULL;
+       if (blacklist->regexp_list) {
+               zend_regexp_list *temp, *it = blacklist->regexp_list;
+               while( it ){
+                       regfree(&it->comp_regex);
+                       temp = it;
+                       it = it->next;
+                       free(temp);
+               }
+       }
+}
+
+static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist)
+{
+       if (blacklist->pos==blacklist->size) {
+               blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE;
+               blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size);
+       }
+}
+
+void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
+{
+       char buf[MAXPATHLEN+1], real_path[MAXPATHLEN+1];
+       FILE *fp;
+       int path_length;
+       TSRMLS_FETCH();
+
+       if ((fp=fopen(filename, "r"))==NULL) {
+               zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename);
+               return;
+       }
+
+       zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file:  '%s'", filename);
+
+       memset(buf, 0, sizeof(buf));
+       memset(real_path, 0, sizeof(real_path));
+
+       while (fgets(buf, MAXPATHLEN, fp)!=NULL) {
+               char *path_dup, *pbuf;
+               path_length = strlen(buf);
+               if (buf[path_length-1]=='\n') {
+                       buf[--path_length] = 0;
+                       if (buf[path_length-1]=='\r') {
+                               buf[--path_length] = 0;
+                       }
+               }
+
+               /* Strip ctrl-m prefix */
+               pbuf = &buf[0];
+               while(*pbuf == '\r') {
+                       *pbuf++ = 0;
+                       path_length--;
+               }
+
+               /* strip \" */
+               if( pbuf[0] == '\"' && pbuf[path_length-1]== '\"' ){
+                       *pbuf++ = 0;
+                       path_length-=2;
+               }
+
+               if (path_length==0) {
+                       continue;
+               }
+
+               path_dup = zend_strndup(pbuf, path_length);
+               expand_filepath(path_dup, real_path TSRMLS_CC);
+               path_length = strlen(real_path);
+
+               free(path_dup);
+
+               zend_accel_blacklist_allocate(blacklist);
+               blacklist->entries[blacklist->pos].path_length = path_length;
+               blacklist->entries[blacklist->pos].path = (char *) malloc(path_length+1);
+               blacklist->entries[blacklist->pos].id = blacklist->pos;
+               memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length+1);
+               blacklist->pos++;
+       }
+       fclose(fp);
+       zend_accel_blacklist_update_regexp(blacklist);
+}
+
+zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path)
+{
+       int ret = 0;
+       zend_regexp_list *regexp_list_it = blacklist->regexp_list;
+
+       if (regexp_list_it == NULL) {
+               return 0;
+       }
+       while (regexp_list_it != NULL) {
+               if (regexec(&(regexp_list_it->comp_regex), verify_path, 0, NULL, 0) == 0) {
+                       ret = 1;
+                       break;
+               }
+               regexp_list_it = regexp_list_it->next;
+       }
+       return ret;
+}
+
+void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC)
+{
+       int i;
+
+       for(i=0; i<blacklist->pos; i++) {
+               func(&blacklist->entries[i], argument TSRMLS_CC);
+       }
+}
diff --git a/zend_accelerator_blacklist.h b/zend_accelerator_blacklist.h
new file mode 100644 (file)
index 0000000..5d7730f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_BLACKLIST_H
+#define ZEND_ACCELERATOR_BLACKLIST_H
+
+typedef struct _zend_regexp_list zend_regexp_list;
+
+typedef struct _zend_blacklist_entry {
+    char *path;
+    int   path_length;
+       int   id;
+} zend_blacklist_entry;
+
+typedef struct _zend_blacklist {
+       zend_blacklist_entry *entries;
+       int                   size;
+       int                   pos;
+       zend_regexp_list     *regexp_list;
+} zend_blacklist;
+
+extern zend_blacklist accel_blacklist;
+
+void zend_accel_blacklist_init(zend_blacklist *blacklist);
+void zend_accel_blacklist_shutdown(zend_blacklist *blacklist);
+
+void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename);
+zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path);
+void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC);
+
+#endif /* ZEND_ACCELERATOR_BLACKLIST_H */
diff --git a/zend_accelerator_debug.c b/zend_accelerator_debug.c
new file mode 100644 (file)
index 0000000..8b2bd62
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#ifdef ZEND_WIN32
+# include <process.h>
+#endif
+#include "ZendAccelerator.h"
+
+void zend_accel_error(int type, const char *format, ...)
+{
+    va_list args;
+       time_t timestamp;
+       char *time_string;
+       FILE * fLog = NULL;
+       TSRMLS_FETCH();
+
+       if (type > ZCG(accel_directives).log_verbosity_level) {
+               return;
+       }
+
+       timestamp = time(NULL);
+       time_string = asctime(localtime(&timestamp));
+       time_string[24] = 0;
+
+       if (!ZCG(accel_directives).error_log ||
+           !*ZCG(accel_directives).error_log ||
+           strcmp(ZCG(accel_directives).error_log, "stderr") == 0) {
+
+               fLog = stderr;
+       } else {
+               fLog = fopen(ZCG(accel_directives).error_log, "a+");
+               if (!fLog) {
+                       fLog = stderr;
+               }
+       }
+
+    fprintf(fLog, "%s (%d): ", time_string,
+#ifdef ZTS
+               tsrm_thread_id()
+#else
+               getpid()
+#endif
+               );
+
+       switch (type) {
+               case ACCEL_LOG_FATAL:
+                       fprintf(fLog, "Fatal Error ");
+                       break;
+               case ACCEL_LOG_ERROR:
+                       fprintf(fLog, "Error ");
+                       break;
+               case ACCEL_LOG_WARNING:
+                       fprintf(fLog, "Warning ");
+                       break;
+               case ACCEL_LOG_INFO:
+                       fprintf(fLog, "Message ");
+                       break;
+               case ACCEL_LOG_DEBUG:
+                       fprintf(fLog, "Debug ");
+                       break;
+       }
+
+    va_start(args, format);
+    vfprintf(fLog, format, args);
+    va_end(args);
+       fprintf(fLog, "\n");
+       switch (type) {
+               case ACCEL_LOG_ERROR:
+                       zend_bailout();
+                       break;
+               case ACCEL_LOG_FATAL:
+                       exit(-2);
+                       break;
+       }
+       fflush(fLog);
+       if (fLog != stderr) {
+               fclose(fLog);
+       }
+}
diff --git a/zend_accelerator_debug.h b/zend_accelerator_debug.h
new file mode 100644 (file)
index 0000000..8d0d732
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_DEBUG_H
+#define ZEND_ACCELERATOR_DEBUG_H
+
+#define ACCEL_LOG_FATAL                                        0
+#define ACCEL_LOG_ERROR                                        1
+#define ACCEL_LOG_WARNING                              2
+#define ACCEL_LOG_INFO                                 3
+#define ACCEL_LOG_DEBUG                                        4
+
+void zend_accel_error(int type, const char *format, ...);
+
+#endif /* _ZEND_ACCELERATOR_DEBUG_H */
diff --git a/zend_accelerator_hash.c b/zend_accelerator_hash.c
new file mode 100644 (file)
index 0000000..8257ac2
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "ZendAccelerator.h"
+#include "zend_accelerator_hash.h"
+#include "zend_hash.h"
+#include "zend_shared_alloc.h"
+
+/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
+static uint prime_numbers[] =
+       {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 };
+static uint num_prime_numbers = sizeof(prime_numbers) / sizeof(uint);
+
+void zend_accel_hash_clean(zend_accel_hash *accel_hash)
+{
+       accel_hash->num_entries = 0;
+       accel_hash->num_direct_entries = 0;
+       memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
+}
+
+void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size)
+{
+       uint i;
+
+       for (i=0; i<num_prime_numbers; i++) {
+               if (hash_size <= prime_numbers[i]) {
+                       hash_size = prime_numbers[i];
+                       break;
+               }
+       }
+
+       accel_hash->num_entries = 0;
+       accel_hash->num_direct_entries = 0;
+       accel_hash->max_num_entries = hash_size;
+
+       /* set up hash pointers table */
+       accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
+       if (!accel_hash->hash_table) {
+               zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+       }
+
+       /* set up hash values table */
+       accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries);
+       if (!accel_hash->hash_entries) {
+               zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+       }
+       memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
+}
+
+/* Returns NULL if hash is full
+ * Returns pointer the actual hash entry on success
+ * key needs to be already allocated as it is not copied
+ */
+zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, char *key, zend_uint key_length, zend_bool indirect, void *data)
+{
+       zend_ulong hash_value;
+       zend_ulong index;
+       zend_accel_hash_entry *entry;
+       zend_accel_hash_entry *indirect_bucket = NULL;
+
+       if (indirect) {
+               indirect_bucket = (zend_accel_hash_entry*)data;
+               while (indirect_bucket->indirect) {
+                       indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data;
+               }
+       }
+
+       hash_value = zend_inline_hash_func(key, key_length);
+       index = hash_value % accel_hash->max_num_entries;
+
+       /* try to see if the element already exists in the hash */
+       entry = accel_hash->hash_table[index];
+       while (entry) {
+               if (entry->hash_value == hash_value
+                       && entry->key_length == key_length
+                       && !memcmp(entry->key, key, key_length)) {
+
+                       if (entry->indirect) {
+                               if (indirect_bucket) {
+                                       entry->data = indirect_bucket;
+                               } else {
+                                       ((zend_accel_hash_entry*)entry->data)->data = data;
+                               }
+                       } else {
+                               if (indirect_bucket) {
+                                       accel_hash->num_direct_entries--;
+                                       entry->data = indirect_bucket;
+                                       entry->indirect = 1;
+                               } else {
+                                       entry->data = data;
+                               }
+                       }
+                       return entry;
+               }
+               entry = entry->next;
+       }
+
+       /* Does not exist, add a new entry */
+       if (accel_hash->num_entries == accel_hash->max_num_entries) {
+               return NULL;
+       }
+
+       entry = &accel_hash->hash_entries[accel_hash->num_entries++];
+       if (indirect) {
+               entry->data = indirect_bucket;
+               entry->indirect = 1;
+       } else {
+               accel_hash->num_direct_entries++;
+               entry->data = data;
+               entry->indirect = 0;
+       }
+       entry->hash_value = hash_value;
+       entry->key = key;
+       entry->key_length = key_length;
+       entry->next = accel_hash->hash_table[index];
+       accel_hash->hash_table[index] = entry;
+       return entry;
+}
+
+/* Returns the data associated with key on success
+ * Returns NULL if data doesn't exist
+ */
+void* zend_accel_hash_find(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
+{
+       zend_ulong hash_value;
+       zend_ulong index;
+       zend_accel_hash_entry *entry;
+
+       hash_value = zend_inline_hash_func(key, key_length);
+       index = hash_value % accel_hash->max_num_entries;
+
+       entry = accel_hash->hash_table[index];
+       while (entry) {
+               if (entry->hash_value == hash_value
+                       && entry->key_length == key_length
+                       && !memcmp(entry->key, key, key_length)) {
+                       if (entry->indirect) {
+                               return ((zend_accel_hash_entry *) entry->data)->data;
+                       } else {
+                               return entry->data;
+                       }
+               }
+               entry = entry->next;
+       }
+       return NULL;
+}
+
+/* Returns the hash entry associated with key on success
+ * Returns NULL if it doesn't exist
+ */
+zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
+{
+       zend_ulong hash_value;
+       zend_ulong index;
+       zend_accel_hash_entry *entry;
+
+       hash_value = zend_inline_hash_func(key, key_length);
+       index = hash_value % accel_hash->max_num_entries;
+
+       entry = accel_hash->hash_table[index];
+       while (entry) {
+               if (entry->hash_value == hash_value
+                       && entry->key_length == key_length
+                       && !memcmp(entry->key, key, key_length)) {
+                       if (entry->indirect) {
+                               return (zend_accel_hash_entry *) entry->data;
+                       } else {
+                               return entry;
+                       }
+               }
+               entry = entry->next;
+       }
+       return NULL;
+}
+
+int zend_accel_hash_unlink(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
+{
+       zend_ulong hash_value;
+    zend_ulong index;
+    zend_accel_hash_entry *entry, *last_entry=NULL;
+
+       hash_value = zend_inline_hash_func(key, key_length);
+       index = hash_value % accel_hash->max_num_entries;
+
+       entry = accel_hash->hash_table[index];
+       while (entry) {
+               if (entry->hash_value == hash_value
+                       && entry->key_length == key_length
+                       && !memcmp(entry->key, key, key_length)) {
+                       if (!entry->indirect) {
+                               accel_hash->num_direct_entries--;
+                       }
+                       if (last_entry) {
+                               last_entry->next = entry->next;
+                       } else {
+                               accel_hash->hash_table[index] = entry->next;
+                       }
+                       return SUCCESS;
+               }
+               last_entry = entry;
+               entry = entry->next;
+       }
+       return FAILURE;
+}
diff --git a/zend_accelerator_hash.h b/zend_accelerator_hash.h
new file mode 100644 (file)
index 0000000..4673a5e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_HASH_H
+#define ZEND_ACCELERATOR_HASH_H
+
+#include "zend.h"
+
+/*
+       zend_accel_hash - is a hash table allocated in shared memory and
+       distributed across simultaneously running processes. The hash tables have
+       fixed sizen selected during construction by zend_accel_hash_init(). All the
+       hash entries are preallocated in the 'hash_entries' array. 'num_entries' is
+       initialized by zero and grows when new data is added.
+       zend_accel_hash_update() just takes the next entry from 'hash_entries'
+       array and puts it into appropriate place of 'hash_table'.
+       Hash collisions are resolved by separate chaining with linked lists,
+       however, entries are still taken from the same 'hash_entries' array.
+       'key' and 'data' passed to zend_accel_hash_update() must be already
+       allocated in shared memory. Few keys may be resolved to the same data.
+       using 'indirect' emtries, that point to other entries ('data' is actually
+       a pointer to another zend_accel_hash_entry).
+       zend_accel_hash_update() requires exclusive lock, however,
+       zend_accel_hash_find() does not.
+*/
+
+typedef struct _zend_accel_hash_entry zend_accel_hash_entry;
+
+struct _zend_accel_hash_entry {
+       zend_ulong             hash_value;
+       char                  *key;
+       zend_uint              key_length;
+       zend_accel_hash_entry *next;
+       void                  *data;
+       zend_bool              indirect;
+};
+
+typedef struct _zend_accel_hash {
+       zend_accel_hash_entry **hash_table;
+       zend_accel_hash_entry  *hash_entries;
+       zend_uint               num_entries;
+       zend_uint               max_num_entries;
+       zend_uint               num_direct_entries;
+} zend_accel_hash;
+
+void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size);
+void zend_accel_hash_clean(zend_accel_hash *accel_hash);
+
+zend_accel_hash_entry* zend_accel_hash_update(
+               zend_accel_hash        *accel_hash,
+               char                   *key,
+               zend_uint               key_length,
+               zend_bool               indirect,
+               void                   *data);
+
+void* zend_accel_hash_find(
+               zend_accel_hash        *accel_hash,
+               char                   *key,
+               zend_uint               key_length);
+
+zend_accel_hash_entry* zend_accel_hash_find_entry(
+               zend_accel_hash        *accel_hash,
+               char                   *key,
+               zend_uint               key_length);
+
+int zend_accel_hash_unlink(
+               zend_accel_hash        *accel_hash,
+               char                   *key,
+               zend_uint               key_length);
+
+static inline zend_bool zend_accel_hash_is_full(zend_accel_hash *accel_hash)
+{
+       if (accel_hash->num_entries == accel_hash->max_num_entries) {
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+#endif /* ZEND_ACCELERATOR_HASH_H */
diff --git a/zend_accelerator_module.c b/zend_accelerator_module.c
new file mode 100644 (file)
index 0000000..b1c90a0
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include <time.h>
+
+#include "php.h"
+#include "ZendAccelerator.h"
+#include "zend_API.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_blacklist.h"
+#include "php_ini.h"
+#include "SAPI.h"
+#include "TSRM/tsrm_virtual_cwd.h"
+#include "ext/standard/info.h"
+#include "ext/standard/php_filestat.h"
+
+#define STRING_NOT_NULL(s) (NULL == (s)?"":s)
+#define MIN_ACCEL_FILES 200
+#define MAX_ACCEL_FILES 100000
+#define TOKENTOSTR(X) #X
+
+/* User functions */
+static ZEND_FUNCTION(accelerator_reset);
+
+/* Private functions */
+static ZEND_FUNCTION(accelerator_get_status);
+static ZEND_FUNCTION(accelerator_get_configuration);
+
+static zend_function_entry accel_functions[] = {
+       /* User functions */
+       ZEND_FE(accelerator_reset,                                      NULL)
+       /* Private functions */
+       ZEND_FE(accelerator_get_configuration,          NULL)
+       ZEND_FE(accelerator_get_status,                         NULL)
+       { NULL, NULL, NULL, 0, 0 }
+};
+
+static ZEND_INI_MH(OnUpdateMemoryConsumption)
+{
+       long *p;
+       long memsize;
+#ifndef ZTS
+       char *base = (char *) mh_arg2;
+#else
+       char *base = (char *) ts_resource(*((int *) mh_arg2));
+#endif
+
+       /* keep the compiler happy */
+       (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
+
+       p = (long *) (base+(size_t) mh_arg1);
+       memsize = atoi(new_value);
+       /* sanity check we must use at least 8 MB */
+       if (memsize < 8) {
+               const char *new_new_value = "8";
+               zend_ini_entry *ini_entry;
+
+               memsize = 8;
+               zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.memory_consumption is set below the required 8MB.\n" );
+               zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB cofiguration.\n" );
+
+               if (zend_hash_find(EG(ini_directives),
+                                       "zend_optimizerplus.memory_consumption",
+                                       sizeof("zend_optimizerplus.memory_consumption"),
+                                       (void *) &ini_entry)==FAILURE) {
+                       return FAILURE;
+               }
+
+               ini_entry->value = strdup(new_new_value);
+               ini_entry->value_length = strlen(new_new_value);
+       }
+       *p = memsize * (1024 * 1024);
+       return SUCCESS;
+}
+
+static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
+{
+       long *p;
+       long size;
+#ifndef ZTS
+       char *base = (char *) mh_arg2;
+#else
+       char *base = (char *) ts_resource(*((int *) mh_arg2));
+#endif
+
+       /* keep the compiler happy */
+       (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
+
+       p = (long *) (base+(size_t) mh_arg1);
+       size = atoi(new_value);
+       /* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */
+
+       if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) {
+               const char *new_new_value;
+               zend_ini_entry *ini_entry;
+
+               if(size < MIN_ACCEL_FILES){
+                       size = MIN_ACCEL_FILES;
+                       new_new_value = TOKENTOSTR(MIN_ACCEL_FILES);
+                       zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES );
+                       zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use the minimal cofiguration.\n" );
+               }
+               if(size > MAX_ACCEL_FILES){
+                       size = MAX_ACCEL_FILES;
+                       new_new_value = TOKENTOSTR(MAX_ACCEL_FILES);
+                       zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES );
+                       zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal cofiguration.\n" );
+               }
+               if (zend_hash_find(EG(ini_directives),
+                                       "zend_optimizerplus.max_accelerated_files",
+                                       sizeof("zend_optimizerplus.max_accelerated_files"),
+                                       (void *) &ini_entry)==FAILURE) {
+                       return FAILURE;
+               }
+               ini_entry->value = strdup(new_new_value);
+               ini_entry->value_length = strlen(new_new_value);
+       }
+       *p = size;
+       return SUCCESS;
+}
+
+static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
+{
+       double *p;
+       long percentage;
+#ifndef ZTS
+       char *base = (char *) mh_arg2;
+#else
+       char *base = (char *) ts_resource(*((int *) mh_arg2));
+#endif
+
+       /* keep the compiler happy */
+       (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
+
+       p = (double *) (base+(size_t) mh_arg1);
+       percentage = atoi(new_value);
+
+       if (percentage <= 0 || percentage > 50) {
+               const char *new_new_value = "5";
+               zend_ini_entry *ini_entry;
+
+               percentage = 5;
+               zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_wasted_percentage must be ser netweeb 1 and 50.\n");
+               zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use 5%.\n" );
+               if (zend_hash_find(EG(ini_directives),
+                                       "zend_optimizerplus.max_wasted_percentage",
+                                       sizeof("zend_optimizerplus.max_wasted_percentage"),
+                                       (void *) &ini_entry)==FAILURE) {
+                       return FAILURE;
+               }
+               ini_entry->value = strdup(new_new_value);
+               ini_entry->value_length = strlen(new_new_value);
+       }
+       *p = (double)percentage / 100.0;
+       return SUCCESS;
+}
+
+static ZEND_INI_MH(OnUpdateAccelBlacklist)
+{
+       char **p;
+#ifndef ZTS
+       char *base = (char *) mh_arg2;
+#else
+       char *base = (char *) ts_resource(*((int *) mh_arg2));
+#endif
+
+       /* keep the compiler happy */
+       (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
+
+       if (new_value && !new_value[0]) {
+               return FAILURE;
+       }
+
+       p = (char **) (base+(size_t) mh_arg1);
+       *p = new_value;
+
+       zend_accel_blacklist_init(&accel_blacklist);
+       zend_accel_blacklist_load(&accel_blacklist, *p);
+
+       return SUCCESS;
+}
+
+ZEND_INI_BEGIN()
+    STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable"             ,"1", PHP_INI_SYSTEM, OnUpdateBool, enabled                             , zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.use_cwd"            ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd            , zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.validate_timestamps","1", PHP_INI_ALL   , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.inherited_hack"     ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack     , zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.dups_fix"           ,"0", PHP_INI_ALL   , OnUpdateBool, accel_directives.ignore_dups        , zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.revalidate_path"    ,"0", PHP_INI_ALL   , OnUpdateBool, accel_directives.revalidate_path    , zend_accel_globals, accel_globals)
+
+       STD_PHP_INI_ENTRY("zend_optimizerplus.log_verbosity_level"   , "1"   , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level,       zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.memory_consumption"    , "64"  , PHP_INI_SYSTEM, OnUpdateMemoryConsumption,    accel_directives.memory_consumption,        zend_accel_globals, accel_globals)
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       STD_PHP_INI_ENTRY("zend_optimizerplus.interned_strings_buffer", "4"  , PHP_INI_SYSTEM, OnUpdateLong,                 accel_directives.interned_strings_buffer,   zend_accel_globals, accel_globals)
+#endif
+       STD_PHP_INI_ENTRY("zend_optimizerplus.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles,      accel_directives.max_accelerated_files,     zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.max_wasted_percentage" , "5"   , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage,      accel_directives.max_wasted_percentage,     zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.consistency_checks"    , "0"   , PHP_INI_ALL   , OnUpdateLong,                 accel_directives.consistency_checks,        zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong,                 accel_directives.force_restart_timeout,     zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.revalidate_freq"       , "2"   , PHP_INI_ALL   , OnUpdateLong,                 accel_directives.revalidate_freq,           zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.preferred_memory_model", ""    , PHP_INI_SYSTEM, OnUpdateStringUnempty,        accel_directives.memory_model,              zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.blacklist_filename"    , ""    , PHP_INI_SYSTEM, OnUpdateAccelBlacklist,       accel_directives.user_blacklist_filename,   zend_accel_globals, accel_globals)
+
+       STD_PHP_INI_ENTRY("zend_optimizerplus.protect_memory"        , "0"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.protect_memory,            zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.save_comments"         , "1"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.save_comments,             zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.fast_shutdown"         , "0"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.fast_shutdown,             zend_accel_globals, accel_globals)
+
+       STD_PHP_INI_ENTRY("zend_optimizerplus.optimization_level"    , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level,   zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_file_override"   , "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.file_override_enabled,     zend_accel_globals, accel_globals)
+       STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_cli"             , "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.enable_cli,                zend_accel_globals, accel_globals)
+       STD_PHP_INI_ENTRY("zend_optimizerplus.error_log"                , ""    , PHP_INI_SYSTEM, OnUpdateString,                accel_directives.error_log,                 zend_accel_globals, accel_globals)
+
+#ifdef ZEND_WIN32
+       STD_PHP_INI_ENTRY("zend_optimizerplus.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString,                              accel_directives.mmap_base,                 zend_accel_globals, accel_globals)
+#endif
+ZEND_INI_END()
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+
+#undef  EX
+#define EX(element) execute_data->element
+#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
+
+static int ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       zend_class_entry **pce, **pce_orig;
+
+       if (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op2.u.constant), Z_STRLEN(EX(opline)->op2.u.constant)+1, (void **)&pce) == FAILURE ||
+           (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op1.u.constant), Z_STRLEN(EX(opline)->op1.u.constant), (void**)&pce_orig) == SUCCESS &&
+            *pce != *pce_orig)) {
+               do_bind_inherited_class(EX(opline), EG(class_table), EX_T(EX(opline)->extended_value).class_entry, 0 TSRMLS_CC);
+       }
+       EX(opline)++;
+       return ZEND_USER_OPCODE_CONTINUE;
+}
+#endif
+
+static int filename_is_in_cache(char *filename, int filename_len TSRMLS_DC)
+{
+       char *key;
+       int key_length;
+       zend_file_handle handle = {0};
+       zend_persistent_script *persistent_script;
+
+       handle.filename = filename;
+       handle.type = ZEND_HANDLE_FILENAME;
+
+       if (IS_ABSOLUTE_PATH(filename, filename_len)) {
+               persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len+1);
+               if (persistent_script) {
+                       return !persistent_script->corrupted;
+               }
+       }
+
+       if((key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC)) != NULL) {
+               persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1);
+               return persistent_script && !persistent_script->corrupted;
+       }
+
+       return 0;
+}
+
+static void accel_file_in_cache(int type, INTERNAL_FUNCTION_PARAMETERS)
+{
+       char *filename;
+       int filename_len;
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       zval **zfilename;
+
+       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &zfilename) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+       convert_to_string_ex(zfilename);
+       filename = Z_STRVAL_PP(zfilename);
+       filename_len = Z_STRLEN_PP(zfilename);
+#else
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+               return;
+       }
+#endif
+       if(filename_len > 0) {
+               if(filename_is_in_cache(filename, filename_len TSRMLS_CC)) {
+                       RETURN_TRUE;
+               }
+       }
+
+       php_stat(filename, filename_len, type, return_value TSRMLS_CC);
+}
+
+static void accel_file_exists(INTERNAL_FUNCTION_PARAMETERS)
+{
+       accel_file_in_cache(FS_EXISTS, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+
+static void accel_is_file(INTERNAL_FUNCTION_PARAMETERS)
+{
+       accel_file_in_cache(FS_IS_FILE, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+
+static void accel_is_readable(INTERNAL_FUNCTION_PARAMETERS)
+{
+       accel_file_in_cache(FS_IS_R, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+
+static ZEND_MINIT_FUNCTION(zend_accelerator)
+{
+       (void)type; /* keep the compiler happy */
+
+       /* must be 0 before the ini entry OnUpdate function is called */
+       accel_blacklist.entries = NULL;
+
+       REGISTER_INI_ENTRIES();
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       zend_set_user_opcode_handler(ZEND_DECLARE_INHERITED_CLASS_DELAYED, ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER);
+#endif
+       return SUCCESS;
+}
+
+void zend_accel_override_file_functions(TSRMLS_D)
+{
+       zend_function *old_function;
+       if(ZCG(startup_ok) && ZCG(accel_directives).file_override_enabled) {
+               /* override file_exists */
+               if(zend_hash_find(CG(function_table), "file_exists", sizeof("file_exists"), (void **)&old_function) == SUCCESS) {
+                       old_function->internal_function.handler = accel_file_exists;
+               }
+               if(zend_hash_find(CG(function_table), "is_file", sizeof("is_file"), (void **)&old_function) == SUCCESS) {
+                       old_function->internal_function.handler = accel_is_file;
+               }
+               if(zend_hash_find(CG(function_table), "is_readable", sizeof("is_readable"), (void **)&old_function) == SUCCESS) {
+                       old_function->internal_function.handler = accel_is_readable;
+               }
+       }
+}
+
+static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)
+{
+       (void)type; /* keep the compiler happy */
+
+       UNREGISTER_INI_ENTRIES();
+       return SUCCESS;
+}
+
+void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+       php_info_print_table_start();
+
+       if (ZCG(startup_ok) && ZCSG(accelerator_enabled)) {
+               php_info_print_table_row(2, "Opcode Caching", "Up and Running");
+       } else {
+               php_info_print_table_row(2, "Opcode Caching", "Disabled");
+       }
+       if (ZCG(enabled) && ZCG(accel_directives).optimization_level) {
+               php_info_print_table_row(2, "Optimization", "Enabled");
+       } else {
+               php_info_print_table_row(2, "Optimization", "Disabled");
+       }
+       if (!ZCG(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");
+               php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
+       }
+
+       php_info_print_table_end();
+       DISPLAY_INI_ENTRIES();
+}
+
+static zend_module_entry accel_module_entry = {
+       STANDARD_MODULE_HEADER,
+       ACCELERATOR_PRODUCT_NAME,
+       accel_functions,
+       ZEND_MINIT(zend_accelerator),
+       ZEND_MSHUTDOWN(zend_accelerator),
+       NULL,
+       NULL,
+       zend_accel_info,
+    ACCELERATOR_VERSION "FE",
+       STANDARD_MODULE_PROPERTIES
+};
+
+int start_accel_module()
+{
+       return zend_startup_module(&accel_module_entry);
+}
+
+/* {{{ proto array accelerator_get_scripts()
+   Get the scripts which are accelerated by ZendAccelerator */
+static zval* accelerator_get_scripts(TSRMLS_D)
+{
+       uint i;
+       zval *return_value,*persistent_script_report;
+       zend_accel_hash_entry *cache_entry;
+       struct tm *ta;
+       struct timeval exec_time;
+       struct timeval fetch_time;
+
+       if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
+               return 0;
+       }
+
+       MAKE_STD_ZVAL(return_value);
+       array_init(return_value);
+       for (i=0; i<ZCSG(hash).max_num_entries; i++) {
+               for (cache_entry=ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) {
+                       zend_persistent_script *script;
+
+                       if (cache_entry->indirect) continue;
+
+                       script = (zend_persistent_script *)cache_entry->data;
+
+                       MAKE_STD_ZVAL(persistent_script_report);
+                       array_init(persistent_script_report);
+                       add_assoc_stringl(persistent_script_report, "full_path", script->full_path, script->full_path_len, 1);
+                       add_assoc_long(persistent_script_report, "hits", script->dynamic_members.hits);
+                       add_assoc_long(persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
+                       ta = localtime(&script->dynamic_members.last_used);
+                       add_assoc_string(persistent_script_report, "last_used", asctime(ta), 1);
+                       add_assoc_long(persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used);
+                       if (ZCG(accel_directives).validate_timestamps) {
+                               add_assoc_long(persistent_script_report, "timestamp", (long)script->timestamp);
+                       }
+                       timerclear(&exec_time);
+                       timerclear(&fetch_time);
+
+                       zend_hash_update(return_value->value.ht, cache_entry->key, cache_entry->key_length, &persistent_script_report, sizeof(zval *), NULL);
+               }
+       }
+       accelerator_shm_read_unlock(TSRMLS_C);
+
+       return return_value;
+}
+
+/* {{{ proto array accelerator_get_status()
+   Obtain statistics information regarding code acceleration in the Zend Performance Suite */
+static ZEND_FUNCTION(accelerator_get_status)
+{
+       long reqs;
+       zval *memory_usage,*statistics,*scripts;
+
+       /* keep the compiler happy */
+       (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
+
+       if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) {
+               RETURN_FALSE;
+       }
+
+       array_init(return_value);
+
+       /* Trivia */
+       add_assoc_long(return_value, "accelerator_enabled", ZCG(startup_ok) && ZCSG(accelerator_enabled));
+       add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
+
+       /* Memory usage statistics */
+       MAKE_STD_ZVAL(memory_usage);
+       array_init(memory_usage);
+       add_assoc_long(memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
+       add_assoc_long(memory_usage, "free_memory", zend_shared_alloc_get_free_memory());
+       add_assoc_long(memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory));
+       add_assoc_double(memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0);
+       add_assoc_zval(return_value, "memory_usage",memory_usage);
+
+       /* Accelerator statistics */
+       MAKE_STD_ZVAL(statistics);
+       array_init(statistics);
+       add_assoc_long(statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries);
+       add_assoc_long(statistics, "max_cached_scripts", ZCSG(hash).max_num_entries);
+       add_assoc_long(statistics, "hits", ZCSG(hits));
+       add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time));
+       add_assoc_long(statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
+       add_assoc_long(statistics, "blacklist_misses", ZCSG(blacklist_misses));
+       reqs = ZCSG(hits)+ZCSG(misses);
+       add_assoc_double(statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0);
+       add_assoc_double(statistics, "accelerator_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0);
+       add_assoc_zval(return_value, "accelerator_statistics",statistics);
+
+       /* acceleratred scripts */
+       scripts=accelerator_get_scripts(TSRMLS_C);
+       if( scripts ){
+               add_assoc_zval(return_value, "scripts",scripts);
+       }
+}
+
+static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value TSRMLS_DC)
+{
+       add_next_index_stringl(return_value, p->path, p->path_length, 1);
+       return 0;
+}
+
+/* {{{ proto array accelerator_get_configuration()
+   Obtain configuration information for the Zend Performance Suite */
+static ZEND_FUNCTION(accelerator_get_configuration)
+{
+       zval *directives,*version,*blacklist;
+
+       /* keep the compiler happy */
+       (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
+
+       array_init(return_value);
+
+       /* directives */
+       MAKE_STD_ZVAL(directives);
+       array_init(directives);
+       add_assoc_bool(directives, "zend_optimizerplus.enable",              ZCG(enabled));
+       add_assoc_bool(directives, "zend_optimizerplus.use_cwd",             ZCG(accel_directives).use_cwd);
+       add_assoc_bool(directives, "zend_optimizerplus.validate_timestamps", ZCG(accel_directives).validate_timestamps);
+       add_assoc_bool(directives, "zend_optimizerplus.inherited_hack",      ZCG(accel_directives).inherited_hack);
+       add_assoc_bool(directives, "zend_optimizerplus.dups_fix",            ZCG(accel_directives).ignore_dups);
+       add_assoc_bool(directives, "zend_optimizerplus.revalidate_path",     ZCG(accel_directives).revalidate_path);
+
+       add_assoc_long(directives,   "zend_optimizerplus.log_verbosity_level",    ZCG(accel_directives).log_verbosity_level);
+       add_assoc_long(directives,       "zend_optimizerplus.memory_consumption",     ZCG(accel_directives).memory_consumption);
+       add_assoc_long(directives,       "zend_optimizerplus.max_accelerated_files",  ZCG(accel_directives).max_accelerated_files);
+       add_assoc_double(directives, "zend_optimizerplus.max_wasted_percentage",  ZCG(accel_directives).max_wasted_percentage);
+       add_assoc_long(directives,       "zend_optimizerplus.consistency_checks",     ZCG(accel_directives).consistency_checks);
+       add_assoc_long(directives,       "zend_optimizerplus.force_restart_timeout",  ZCG(accel_directives).force_restart_timeout);
+       add_assoc_long(directives,       "zend_optimizerplus.revalidate_freq",        ZCG(accel_directives).revalidate_freq);
+       add_assoc_string(directives, "zend_optimizerplus.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model), 1);
+       add_assoc_string(directives, "zend_optimizerplus.blacklist_filename",     STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename), 1);
+
+       add_assoc_bool(directives,   "zend_optimizerplus.protect_memory",         ZCG(accel_directives).protect_memory);
+       add_assoc_bool(directives,   "zend_optimizerplus.save_comments",          ZCG(accel_directives).save_comments);
+       add_assoc_bool(directives,   "zend_optimizerplus.fast_shutdown",          ZCG(accel_directives).fast_shutdown);
+
+       add_assoc_long(directives,       "zend_optimizerplus.optimization_level",         ZCG(accel_directives).optimization_level);
+
+       add_assoc_zval(return_value,"directives",directives);
+
+       /*version */
+       MAKE_STD_ZVAL(version);
+       array_init(version);
+       add_assoc_string(version, "version", ACCELERATOR_VERSION, 1);
+       add_assoc_string(version, "accelerator_product_name", ACCELERATOR_PRODUCT_NAME, 1);
+       add_assoc_zval(return_value,"version",version);
+
+       /* blacklist */
+       MAKE_STD_ZVAL(blacklist);
+       array_init(blacklist);
+       zend_accel_blacklist_apply(&accel_blacklist, (apply_func_arg_t) add_blacklist_path, blacklist TSRMLS_CC);
+       add_assoc_zval(return_value,"blacklist",blacklist);
+}
+
+/* {{{ proto void accelerator_reset()
+   Request that the contents of the Accelerator module in the ZPS be reset */
+static ZEND_FUNCTION(accelerator_reset)
+{
+       /* keep the compiler happy */
+       (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
+
+       if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) {
+               RETURN_FALSE;
+       }
+
+       zend_accel_schedule_restart(TSRMLS_C);
+       RETURN_TRUE;
+}
diff --git a/zend_accelerator_module.h b/zend_accelerator_module.h
new file mode 100644 (file)
index 0000000..25ad4ae
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERAROR_MODULE_H
+#define ZEND_ACCELERATOR_MODULE_H
+
+int start_accel_module();
+void zend_accel_override_file_functions(TSRMLS_D);
+
+#endif /* _ZEND_ACCELERATOR_MODULE_H */
diff --git a/zend_accelerator_util_funcs.c b/zend_accelerator_util_funcs.c
new file mode 100644 (file)
index 0000000..275642b
--- /dev/null
@@ -0,0 +1,1079 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_accelerator_util_funcs.h"
+#include "zend_persist.h"
+#include "zend_shared_alloc.h"
+
+#define ZEND_PROTECTED_REFCOUNT        (1<<30)
+
+static zend_uint zend_accel_refcount = ZEND_PROTECTED_REFCOUNT;
+
+#if SIZEOF_SIZE_T <= SIZEOF_LONG
+/* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */
+# define accel_xlat_set(old, new)      zend_hash_index_update(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), &(new), sizeof(void*), NULL)
+# define accel_xlat_get(old, new)      zend_hash_index_find(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), (void**)&(new))
+#else
+# define accel_xlat_set(old, new)      zend_hash_quick_add(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new), sizeof(void*), NULL)
+# define accel_xlat_get(old, new)      zend_hash_quick_find(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new))
+#endif
+
+typedef int (*id_function_t)(void *, void *);
+typedef void (*unique_copy_ctor_func_t)(void *pElement);
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+static const Bucket *uninitialized_bucket = NULL;
+#endif
+
+static int zend_prepare_function_for_execution(zend_op_array *op_array);
+static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind);
+
+static void zend_accel_destroy_zend_function(zend_function *function)
+{
+       TSRMLS_FETCH();
+
+       if (function->type == ZEND_USER_FUNCTION) {
+               if (function->op_array.static_variables) {
+
+                       efree(function->op_array.static_variables);
+                       function->op_array.static_variables = NULL;
+               }
+       }
+
+       destroy_zend_function(function TSRMLS_CC);
+}
+
+static void zend_accel_destroy_zend_class(zend_class_entry **pce)
+{
+       zend_class_entry *ce = *pce;
+
+       ce->function_table.pDestructor = (dtor_func_t) zend_accel_destroy_zend_function;
+       destroy_zend_class(pce);
+}
+
+zend_persistent_script* create_persistent_script(void)
+{
+       zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script));
+       memset(persistent_script, 0, sizeof(zend_persistent_script));
+
+       zend_hash_init(&persistent_script->function_table, 100, NULL, (dtor_func_t) zend_accel_destroy_zend_function, 0);
+       /* class_table is usualy destroyed by free_persistent_script() that
+        * overrides destructor. ZEND_CLASS_DTOR may be used by standard
+        * PHP compiler
+        */
+       zend_hash_init(&persistent_script->class_table, 10, NULL, ZEND_CLASS_DTOR, 0);
+
+       return persistent_script;
+}
+
+void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements)
+{
+       if (destroy_elements) {
+               persistent_script->function_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_function;
+               persistent_script->class_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_class;
+       } else {
+               persistent_script->function_table.pDestructor = NULL;
+               persistent_script->class_table.pDestructor = NULL;
+       }
+
+       zend_hash_destroy(&persistent_script->function_table);
+       zend_hash_destroy(&persistent_script->class_table);
+
+       if (persistent_script->full_path) {
+               efree(persistent_script->full_path);
+       }
+
+       efree(persistent_script);
+}
+
+static int is_not_internal_function(zend_function *function)
+{
+       return(function->type != ZEND_INTERNAL_FUNCTION);
+}
+
+void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC)
+{
+       dtor_func_t orig_dtor = ht->pDestructor;
+
+       ht->pDestructor = NULL;
+       zend_hash_apply(ht, (apply_func_t) is_not_internal_function TSRMLS_CC);
+       ht->pDestructor = orig_dtor;
+}
+
+static int move_user_function(zend_function *function TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       HashTable *function_table = va_arg(args, HashTable *);
+       (void)num_args; /* keep the compiler happy */
+
+       if (function->type==ZEND_USER_FUNCTION) {
+               zend_hash_quick_update(function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, function, sizeof(zend_function), NULL);
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+void zend_accel_move_user_functions(HashTable *src, HashTable *dst TSRMLS_DC)
+{
+       dtor_func_t orig_dtor = src->pDestructor;
+
+       src->pDestructor = NULL;
+       zend_hash_apply_with_arguments(src TSRMLS_CC, (apply_func_args_t)move_user_function, 1, dst);
+       src->pDestructor = orig_dtor;
+}
+
+static int copy_internal_function(zend_function *function, HashTable *function_table TSRMLS_DC)
+{
+       if (function->type==ZEND_INTERNAL_FUNCTION) {
+               zend_hash_update(function_table, function->common.function_name, strlen(function->common.function_name)+1, function, sizeof(zend_function), NULL);
+       }
+       return 0;
+}
+
+void zend_accel_copy_internal_functions(TSRMLS_D)
+{
+       zend_hash_apply_with_argument(CG(function_table), (apply_func_arg_t)copy_internal_function, &ZCG(function_table) TSRMLS_CC);
+       ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table));
+}
+
+static void zend_destroy_property_info(zend_property_info *property_info)
+{
+       interned_efree((char*)property_info->name);
+       if(property_info->doc_comment){
+               efree((char*)property_info->doc_comment);
+       }
+}
+
+static inline zval* zend_clone_zval(zval *src, int bind TSRMLS_DC)
+{
+       zval *ret, **ret_ptr;
+
+       if (!bind) {
+               ALLOC_ZVAL(ret);
+               *ret = *src;
+               INIT_PZVAL(ret);
+       } else if (Z_REFCOUNT_P(src) == 1) {
+               ALLOC_ZVAL(ret);
+               *ret = *src;
+       } else if (accel_xlat_get(src, ret_ptr) != SUCCESS) {
+               ALLOC_ZVAL(ret);
+               *ret = *src;
+               accel_xlat_set(src, ret);
+       } else {
+               return *ret_ptr;
+       }
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       if ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) {
+               switch ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK)) {
+#else
+       if ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) {
+               switch ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX)) {
+#endif
+                       case IS_STRING:
+                   case IS_CONSTANT:
+                               Z_STRVAL_P(ret) = (char *) interned_estrndup(Z_STRVAL_P(ret), Z_STRLEN_P(ret));
+                               break;
+                       case IS_ARRAY:
+                   case IS_CONSTANT_ARRAY:
+                               if (ret->value.ht && ret->value.ht != &EG(symbol_table)) {
+                                       ALLOC_HASHTABLE(ret->value.ht);
+                                       zend_hash_clone_zval(ret->value.ht, src->value.ht, 0);
+                               }
+                               break;
+               }
+       }
+       return ret;
+}
+
+static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind)
+{
+       Bucket *p, *q, **prev;
+       ulong nIndex;
+       zval *ppz;
+       TSRMLS_FETCH();
+
+       ht->nTableSize = source->nTableSize;
+       ht->nTableMask = source->nTableMask;
+       ht->nNumOfElements = source->nNumOfElements;
+       ht->nNextFreeElement = source->nNextFreeElement;
+       ht->pDestructor = ZVAL_PTR_DTOR;
+#if ZEND_DEBUG
+       ht->inconsistent = 0;
+#endif
+       ht->persistent = 0;
+       ht->arBuckets = NULL;
+       ht->pListHead = NULL;
+       ht->pListTail = NULL;
+       ht->pInternalPointer = NULL;
+       ht->nApplyCount = 0;
+       ht->bApplyProtection = 1;
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (!ht->nTableMask) {
+               ht->arBuckets = (Bucket**)&uninitialized_bucket;
+               return;
+       }
+#endif
+
+       ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));
+
+       prev = &ht->pListHead;
+       p = source->pListHead;
+       while (p) {
+               nIndex = p->h & ht->nTableMask;
+
+               /* Create bucket and initialize key */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (!p->nKeyLength) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = NULL;
+               } else if (IS_INTERNED(p->arKey)) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = p->arKey;
+               } else {
+                       q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
+                       q->arKey = ((char*)q) + sizeof(Bucket);
+                       memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
+               }
+#else
+               q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
+               if (p->nKeyLength) {
+                       memcpy(q->arKey, p->arKey, p->nKeyLength);
+               }
+#endif
+               q->h = p->h;
+               q->nKeyLength = p->nKeyLength;
+
+               /* Insert into hash collision list */
+               q->pNext = ht->arBuckets[nIndex];
+               q->pLast = NULL;
+               if (q->pNext) {
+                       q->pNext->pLast = q;
+               }
+               ht->arBuckets[nIndex] = q;
+
+               /* Insert into global list */
+               q->pListLast = ht->pListTail;
+               ht->pListTail = q;
+               q->pListNext = NULL;
+               *prev = q;
+               prev = &q->pListNext;
+
+               /* Copy data */
+               q->pData = &q->pDataPtr;
+               if (!bind) {
+                       ALLOC_ZVAL(ppz);
+                       *ppz = *((zval*)p->pDataPtr);
+                       INIT_PZVAL(ppz);
+               } else if (Z_REFCOUNT_P((zval*)p->pDataPtr) == 1) {
+                       ALLOC_ZVAL(ppz);
+                       *ppz = *((zval*)p->pDataPtr);
+               } else if (accel_xlat_get(p->pDataPtr, ppz) != SUCCESS) {
+                       ALLOC_ZVAL(ppz);
+                       *ppz = *((zval*)p->pDataPtr);
+                       accel_xlat_set(p->pDataPtr, ppz);
+               } else {
+                       q->pDataPtr = *(void**)ppz;
+                       p = p->pListNext;
+                       continue;
+               }
+               q->pDataPtr = (void*)ppz;
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+               if ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) {
+                       switch ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK)) {
+#else
+               if ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) {
+                       switch ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX)) {
+#endif
+                               case IS_STRING:
+                           case IS_CONSTANT:
+                                       Z_STRVAL_P(ppz) = (char *) interned_estrndup(Z_STRVAL_P((zval*)p->pDataPtr), Z_STRLEN_P((zval*)p->pDataPtr));
+                                       break;
+                               case IS_ARRAY:
+                           case IS_CONSTANT_ARRAY:
+                                       if (((zval*)p->pDataPtr)->value.ht && ((zval*)p->pDataPtr)->value.ht != &EG(symbol_table)) {
+                                               ALLOC_HASHTABLE(ppz->value.ht);
+                                               zend_hash_clone_zval(ppz->value.ht, ((zval*)p->pDataPtr)->value.ht, 0);
+                                       }
+                                       break;
+                       }
+               }
+
+               p = p->pListNext;
+       }
+       ht->pInternalPointer = ht->pListHead;
+}
+
+static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC)
+{
+       Bucket *p, *q, **prev;
+       ulong nIndex;
+       zend_class_entry **new_ce;
+       zend_function** new_prototype;
+       zend_op_array *new_entry;
+
+       ht->nTableSize = source->nTableSize;
+       ht->nTableMask = source->nTableMask;
+       ht->nNumOfElements = source->nNumOfElements;
+       ht->nNextFreeElement = source->nNextFreeElement;
+       ht->pDestructor = ZEND_FUNCTION_DTOR;
+#if ZEND_DEBUG
+       ht->inconsistent = 0;
+#endif
+       ht->persistent = 0;
+       ht->pListHead = NULL;
+       ht->pListTail = NULL;
+       ht->pInternalPointer = NULL;
+       ht->nApplyCount = 0;
+       ht->bApplyProtection = 1;
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (!ht->nTableMask) {
+               ht->arBuckets = (Bucket**)&uninitialized_bucket;
+               return;
+       }
+#endif
+
+       ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));
+
+       prev = &ht->pListHead;
+       p = source->pListHead;
+       while (p) {
+               nIndex = p->h & ht->nTableMask;
+
+               /* Create bucket and initialize key */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (!p->nKeyLength) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = NULL;
+               } else if (IS_INTERNED(p->arKey)) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = p->arKey;
+               } else {
+                       q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
+                       q->arKey = ((char*)q) + sizeof(Bucket);
+                       memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
+               }
+#else
+               q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
+               if (p->nKeyLength) {
+                       memcpy(q->arKey, p->arKey, p->nKeyLength);
+               }
+#endif
+               q->h = p->h;
+               q->nKeyLength = p->nKeyLength;
+
+               /* Insert into hash collision list */
+               q->pNext = ht->arBuckets[nIndex];
+               q->pLast = NULL;
+               if (q->pNext) {
+                       q->pNext->pLast = q;
+               }
+               ht->arBuckets[nIndex] = q;
+
+               /* Insert into global list */
+               q->pListLast = ht->pListTail;
+               ht->pListTail = q;
+               q->pListNext = NULL;
+               *prev = q;
+               prev = &q->pListNext;
+
+               /* Copy data */
+               q->pData = (void *) emalloc(sizeof(zend_function));
+               new_entry = (zend_op_array*)q->pData;
+               *new_entry = *(zend_op_array*)p->pData;
+               q->pDataPtr=NULL;
+
+               /* Copy constructor */
+               /* we use refcount to show that op_array is referenced from several places */
+               if (new_entry->refcount != NULL) {
+                       accel_xlat_set(p->pData, new_entry);
+               }
+
+               zend_prepare_function_for_execution(new_entry);
+
+               if(old_ce == new_entry->scope) {
+                       new_entry->scope = ce;
+               } else {
+                       if(accel_xlat_get(new_entry->scope, new_ce) == SUCCESS) {
+                               new_entry->scope = *new_ce;
+                       } else {
+                               zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name);
+                       }
+               }
+
+               /* update prototype */
+               if (new_entry->prototype ){
+                       if(accel_xlat_get(new_entry->prototype, new_prototype)==SUCCESS) {
+                               new_entry->prototype = *new_prototype;
+                       } else {
+                               zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name);
+                       }
+               }
+
+               p = p->pListNext;
+       }
+       ht->pInternalPointer = ht->pListHead;
+}
+
+static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC)
+{
+       Bucket *p, *q, **prev;
+       ulong nIndex;
+       zend_class_entry **new_ce;
+       zend_property_info *prop_info;
+
+       ht->nTableSize = source->nTableSize;
+       ht->nTableMask = source->nTableMask;
+       ht->nNumOfElements = source->nNumOfElements;
+       ht->nNextFreeElement = source->nNextFreeElement;
+       ht->pDestructor = (dtor_func_t) zend_destroy_property_info;
+#if ZEND_DEBUG
+       ht->inconsistent = 0;
+#endif
+       ht->persistent = 0;
+       ht->pListHead = NULL;
+       ht->pListTail = NULL;
+       ht->pInternalPointer = NULL;
+       ht->nApplyCount = 0;
+       ht->bApplyProtection = 1;
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (!ht->nTableMask) {
+               ht->arBuckets = (Bucket**)&uninitialized_bucket;
+               return;
+       }
+#endif
+
+       ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));
+
+       prev = &ht->pListHead;
+       p = source->pListHead;
+       while (p) {
+               nIndex = p->h & ht->nTableMask;
+
+               /* Create bucket and initialize key */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (!p->nKeyLength) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = NULL;
+               } else if (IS_INTERNED(p->arKey)) {
+                       q = (Bucket *) emalloc(sizeof(Bucket));
+                       q->arKey = p->arKey;
+               } else {
+                       q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
+                       q->arKey = ((char*)q) + sizeof(Bucket);
+                       memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
+               }
+#else
+               q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
+               if (p->nKeyLength) {
+                       memcpy(q->arKey, p->arKey, p->nKeyLength);
+               }
+#endif
+               q->h = p->h;
+               q->nKeyLength = p->nKeyLength;
+
+               /* Insert into hash collision list */
+               q->pNext = ht->arBuckets[nIndex];
+               q->pLast = NULL;
+               if (q->pNext) {
+                       q->pNext->pLast = q;
+               }
+               ht->arBuckets[nIndex] = q;
+
+               /* Insert into global list */
+               q->pListLast = ht->pListTail;
+               ht->pListTail = q;
+               q->pListNext = NULL;
+               *prev = q;
+               prev = &q->pListNext;
+
+               /* Copy data */
+               q->pData = (void *) emalloc(sizeof(zend_property_info));
+               prop_info = q->pData;
+               *prop_info = *(zend_property_info*)p->pData;
+               q->pDataPtr=NULL;
+
+               /* Copy constructor */
+               prop_info->name = interned_estrndup(prop_info->name, prop_info->name_length);
+               if(prop_info->doc_comment) {
+                       prop_info->doc_comment = estrndup(prop_info->doc_comment, prop_info->doc_comment_len);
+               }
+               if(prop_info->ce == old_ce) {
+                       prop_info->ce = ce;
+               } else if(accel_xlat_get(prop_info->ce, new_ce) == SUCCESS) {
+                       prop_info->ce = *new_ce;
+               } else {
+                       zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s, property %s. Please call Zend Support", ce->name, prop_info->name);
+               }
+
+               p = p->pListNext;
+       }
+       ht->pInternalPointer = ht->pListHead;
+}
+
+/* protects reference count, creates copy of statics */
+static int zend_prepare_function_for_execution(zend_op_array *op_array)
+{
+       HashTable *shared_statics = op_array->static_variables;
+
+       /* protect reference count */
+       op_array->refcount = &zend_accel_refcount;
+       (*op_array->refcount) = ZEND_PROTECTED_REFCOUNT;
+
+       /* copy statics */
+       if (shared_statics) {
+               ALLOC_HASHTABLE(op_array->static_variables);
+               zend_hash_clone_zval(op_array->static_variables, shared_statics, 0);
+       }
+
+       return 0;
+}
+
+#define zend_update_inherited_handler(handler) \
+{ \
+       if(ce->handler != NULL) { \
+               if(accel_xlat_get(ce->handler, new_func)==SUCCESS) { \
+                       ce->handler = *new_func; \
+               } else { \
+                       zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s. Please call Zend Support", ce->name); \
+               } \
+       } \
+}
+
+/* Protects class' refcount, copies default properties, functions and class name */
+static void zend_class_copy_ctor(zend_class_entry **pce)
+{
+       zend_class_entry *ce = *pce;
+       zend_class_entry *old_ce = ce;
+       zend_class_entry **new_ce;
+       zend_function **new_func;
+       TSRMLS_FETCH();
+
+       *pce = ce = emalloc(sizeof(zend_class_entry));
+       *ce = *old_ce;
+       ce->refcount = 1;
+
+       if (old_ce->refcount != 1) {
+               /* this class is not used as a parent for any other classes */
+               accel_xlat_set(old_ce, ce);
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (old_ce->default_properties_table) {
+               int i;
+
+               ce->default_properties_table = emalloc(sizeof(zval*) * old_ce->default_properties_count);
+               for (i = 0; i < old_ce->default_properties_count; i++) {
+                       if (old_ce->default_properties_table[i]) {
+                               ce->default_properties_table[i] = zend_clone_zval(old_ce->default_properties_table[i], 0 TSRMLS_CC);
+                       } else {
+                               ce->default_properties_table[i] = NULL;
+                       }
+               }
+       }
+#else
+       zend_hash_clone_zval(&ce->default_properties, &old_ce->default_properties, 0);
+#endif
+
+       zend_hash_clone_methods(&ce->function_table, &old_ce->function_table, old_ce, ce TSRMLS_CC);
+
+       /* static members */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (old_ce->default_static_members_table) {
+               int i;
+
+               ce->default_static_members_table = emalloc(sizeof(zval*) * old_ce->default_static_members_count);
+               for (i = 0; i < old_ce->default_static_members_count; i++) {
+                       if (old_ce->default_static_members_table[i]) {
+                               ce->default_static_members_table[i] = zend_clone_zval(old_ce->default_static_members_table[i], 1 TSRMLS_CC);
+                       } else {
+                               ce->default_static_members_table[i] = NULL;
+                       }
+               }
+       }
+       ce->static_members_table = ce->default_static_members_table;
+#else
+       zend_hash_clone_zval(&ce->default_static_members, &old_ce->default_static_members, 1);
+       ce->static_members = &ce->default_static_members;
+#endif
+
+       /* properties_info */
+       zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce, ce TSRMLS_CC);
+
+       /* constants table */
+       zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 0);
+
+       ce->name = interned_estrndup(ce->name, ce->name_length);
+
+       /* interfaces aren't really implemented, so we create a new table */
+       if(ce->num_interfaces) {
+               ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces);
+               memset(ce->interfaces, 0, sizeof(zend_class_entry *) * ce->num_interfaces);
+       } else {
+               ce->interfaces = NULL;
+       }
+       if (ZEND_CE_DOC_COMMENT(ce)) {
+               ZEND_CE_DOC_COMMENT(ce) = estrndup(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce));
+       }
+
+       if(ce->parent) {
+               if(accel_xlat_get(ce->parent, new_ce)==SUCCESS) {
+                       ce->parent = *new_ce;
+               } else {
+                       zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s. Please call Zend Support", ce->name);
+               }
+       }
+
+       zend_update_inherited_handler(constructor);
+       zend_update_inherited_handler(destructor);
+       zend_update_inherited_handler(clone);
+       zend_update_inherited_handler(__get);
+       zend_update_inherited_handler(__set);
+       zend_update_inherited_handler(__call);
+/* 5.1 stuff */
+       zend_update_inherited_handler(serialize_func);
+       zend_update_inherited_handler(unserialize_func);
+       zend_update_inherited_handler(__isset);
+       zend_update_inherited_handler(__unset);
+/* 5.2 stuff */
+       zend_update_inherited_handler(__tostring);
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+/* 5.3 stuff */
+       zend_update_inherited_handler(__callstatic);
+#endif
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+/* 5.4 traits */
+       if (ce->trait_aliases) {
+               zend_trait_alias **trait_aliases;
+               int i = 0;
+
+               while (ce->trait_aliases[i]) {
+                       i++;
+               }
+               trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i+1));
+               i = 0;
+               while (ce->trait_aliases[i]) {
+                       trait_aliases[i] = emalloc(sizeof(zend_trait_alias));
+                       memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias));
+                       trait_aliases[i]->trait_method = emalloc(sizeof(zend_trait_method_reference));
+                       memcpy(trait_aliases[i]->trait_method, ce->trait_aliases[i]->trait_method, sizeof(zend_trait_method_reference));
+                       if (trait_aliases[i]->trait_method) {
+                               if (trait_aliases[i]->trait_method->method_name) {
+                                       trait_aliases[i]->trait_method->method_name =
+                                               estrndup(trait_aliases[i]->trait_method->method_name,
+                                                       trait_aliases[i]->trait_method->mname_len);
+                               }
+                               if (trait_aliases[i]->trait_method->class_name) {
+                                       trait_aliases[i]->trait_method->class_name =
+                                               estrndup(trait_aliases[i]->trait_method->class_name,
+                                                       trait_aliases[i]->trait_method->cname_len);
+                               }
+                       }
+
+                       if (trait_aliases[i]->alias) {
+                               trait_aliases[i]->alias =
+                                       estrndup(trait_aliases[i]->alias,
+                                               trait_aliases[i]->alias_len);
+                       }
+                       i++;
+               }
+               trait_aliases[i] = NULL;
+               ce->trait_aliases = trait_aliases;
+       }
+
+       if (ce->trait_precedences) {
+               zend_trait_precedence **trait_precedences;
+               int i = 0;
+
+               while (ce->trait_precedences[i]) {
+                       i++;
+               }
+               trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i+1));
+               i = 0;
+               while (ce->trait_precedences[i]) {
+                       trait_precedences[i] = emalloc(sizeof(zend_trait_precedence));
+                       memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence));
+                       trait_precedences[i]->trait_method = emalloc(sizeof(zend_trait_method_reference));
+                       memcpy(trait_precedences[i]->trait_method, ce->trait_precedences[i]->trait_method, sizeof(zend_trait_method_reference));
+
+                       trait_precedences[i]->trait_method->method_name =
+                               estrndup(trait_precedences[i]->trait_method->method_name,
+                                       trait_precedences[i]->trait_method->mname_len);
+                       trait_precedences[i]->trait_method->class_name =
+                               estrndup(trait_precedences[i]->trait_method->class_name,
+                                       trait_precedences[i]->trait_method->cname_len);
+
+                       if (trait_precedences[i]->exclude_from_classes) {
+                               zend_class_entry **exclude_from_classes;
+                               int j = 0;
+
+                               while (trait_precedences[i]->exclude_from_classes[j]) {
+                                       j++;
+                               }
+                               exclude_from_classes = emalloc(sizeof(zend_class_entry*) * (j+1));
+                               j = 0;
+                               while (trait_precedences[i]->exclude_from_classes[j]) {
+                                       exclude_from_classes[j] = (zend_class_entry*)estrndup(
+                                               (char*)trait_precedences[i]->exclude_from_classes[j],
+                                               strlen((char*)trait_precedences[i]->exclude_from_classes[j]));
+                                       j++;
+                               }
+                               exclude_from_classes[j] = NULL;
+                               trait_precedences[i]->exclude_from_classes = exclude_from_classes;
+                       }
+                       i++;
+               }
+               trait_precedences[i] = NULL;
+               ce->trait_precedences = trait_precedences;
+       }
+#endif
+}
+
+static int zend_accel_same_function(void *f1, void *f2)
+{
+       zend_function *func1 = (zend_function *)f1, *func2 = (zend_function *)f2;
+       zend_op_array *op1 =  (zend_op_array *)func1;
+       zend_op_array *op2 =  (zend_op_array *)func2;
+
+       if(func1->type != ZEND_USER_FUNCTION || func2->type != ZEND_USER_FUNCTION) {
+               return 0;
+       }
+
+       if(func1 == func2) {
+               return 1;
+       }
+
+       if(op1->function_name != op2->function_name && (!op1->function_name || !op2->function_name || strcmp(op1->function_name, op2->function_name))) {
+               /* compare filenames */
+               return 0;
+       }
+
+       if(op1->filename != op2->filename && (!op1->filename || !op2->filename || strcmp(op1->filename, op2->filename))) {
+               /* compare filenames */
+               return 0;
+       }
+
+       if(!op1->opcodes || !op2->opcodes || op1->opcodes[0].lineno != op2->opcodes[0].lineno) {
+               /* compare first lines */
+               return 0;
+       }
+
+       /* if everything matches - we are OK */
+       return 1;
+}
+
+static int zend_accel_same_class(void *f1, void *f2)
+{
+       zend_class_entry *ce1 = *(zend_class_entry **)f1;
+       zend_class_entry *ce2 = *(zend_class_entry **)f2;
+
+       if(ce1->type != ZEND_USER_CLASS || ce2->type != ZEND_USER_CLASS) {
+               return 0;
+       }
+
+       if(ce1 == ce2) {
+               return 1;
+       }
+
+       if(!ce1->name || !ce2->name || zend_binary_strcmp(ce1->name, ce1->name_length, ce2->name, ce2->name_length)) {
+               return 0;
+       }
+
+       if (zend_hash_num_elements(&ce1->function_table) != zend_hash_num_elements(&ce2->function_table)
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+        || ce1->default_properties_count != ce2->default_properties_count) {
+#else
+        || zend_hash_num_elements(&ce1->default_properties) != zend_hash_num_elements(&ce2->default_properties)) {
+#endif
+               return 0;
+       }
+
+       if(zend_hash_num_elements(&ce1->function_table)) {
+               HashPosition pos;
+               zend_function *func1, *func2;
+
+               zend_hash_internal_pointer_reset_ex(&ce1->function_table, &pos);
+               zend_hash_get_current_data_ex(&ce1->function_table, (void **)&func1, &pos);
+               zend_hash_internal_pointer_reset_ex(&ce2->function_table, &pos);
+               zend_hash_get_current_data_ex(&ce2->function_table, (void **)&func2, &pos);
+               if(!zend_accel_same_function(func1, func2)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int zend_hash_unique_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor, uint size, const char **key_fail, ulong *key_fail_l, id_function_t pIdFunc)
+{
+       Bucket *p;
+       void *t;
+
+    p = source->pListHead;
+       while (p) {
+               if (p->nKeyLength>0) {
+                       if (zend_hash_quick_add(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t) ==SUCCESS) {
+                               if(pCopyConstructor) {
+                                       pCopyConstructor(t);
+                               }
+                       } else {
+                               if(p->nKeyLength>0 && p->arKey[0]==0) {
+                                       /* Mangled key, ignore and wait for runtime */
+                               } else if(zend_hash_quick_find(target, p->arKey, p->nKeyLength, p->h, &t) == SUCCESS &&
+                                                 (!pIdFunc || pIdFunc(p->pData, t))) {
+                                       /* same one, ignore it */
+                               } else {
+                                       if(key_fail) {
+                                               *key_fail = p->arKey;
+                                       }
+                                       if(key_fail_l) {
+                                               *key_fail_l = p->nKeyLength;
+                                       }
+                                       return FAILURE;
+                               }
+                       }
+               } else {
+                       if (!zend_hash_index_exists(target, p->h) && zend_hash_index_update(target, p->h, p->pData, size, &t) == SUCCESS) {
+                                       if(pCopyConstructor) {
+                                               pCopyConstructor(t);
+                                       }
+                       } else {
+                               if(key_fail) {
+                                       *key_fail = NULL;
+                               }
+                               if(key_fail_l) {
+                                       *key_fail_l = p->h;
+                               }
+                               return FAILURE;
+                       }
+               }
+               p = p->pListNext;
+       }
+       target->pInternalPointer = target->pListHead;
+
+       return SUCCESS;
+}
+
+static void zend_accel_function_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor) {
+       const char *name;
+       ulong name_len;
+       zend_function *function;
+       TSRMLS_FETCH();
+
+       if(zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_function), &name, &name_len, ZCG(accel_directives).ignore_dups?NULL:zend_accel_same_function) != SUCCESS) {
+               if (zend_hash_find(target, name, name_len, (void *) &function)==SUCCESS
+                       && function->type==ZEND_USER_FUNCTION
+                       && ((zend_op_array *) function)->last>0) {
+                       zend_error(E_ERROR, "Cannot redeclare function %.*s() (previously declared in %s:%d). If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your ini file",
+                                          (int)name_len, name,
+                                          ((zend_op_array *) function)->filename,
+                                          (int)((zend_op_array *) function)->opcodes[0].lineno);
+               } else {
+
+                       zend_error(E_ERROR, "Cannot redeclare function %.*s(). If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your ini file", (int)name_len, name);
+               }
+       }
+}
+
+static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor TSRMLS_DC) {
+       const char *name;
+       ulong name_len;
+       uint size;
+
+       size = sizeof(zend_class_entry*);
+       if(zend_hash_unique_copy(target, source, pCopyConstructor, size, &name, &name_len, ZCG(accel_directives).ignore_dups?NULL:zend_accel_same_class) != SUCCESS) {
+               zend_error(E_ERROR, "Cannot redeclare class %.*s. If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your php.ini", (int)name_len, name);
+       }
+}
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+static void zend_do_delayed_early_binding(zend_op_array *op_array, zend_uint early_binding TSRMLS_DC)
+{
+       zend_uint opline_num = early_binding;
+
+       if ((int)opline_num != -1) {
+               zend_bool orig_in_compilation = CG(in_compilation);
+               char *orig_compiled_filename = zend_set_compiled_filename(op_array->filename TSRMLS_CC);
+               zend_class_entry **pce;
+
+               CG(in_compilation) = 1;
+               while ((int)opline_num != -1) {
+                       if (zend_lookup_class(Z_STRVAL(op_array->opcodes[opline_num-1].op2.u.constant), Z_STRLEN(op_array->opcodes[opline_num-1].op2.u.constant), &pce TSRMLS_CC) == SUCCESS) {
+                               do_bind_inherited_class(&op_array->opcodes[opline_num], EG(class_table), *pce, 1 TSRMLS_CC);
+                       }
+                       opline_num = op_array->opcodes[opline_num].result.u.opline_num;
+               }
+               zend_restore_compiled_filename(orig_compiled_filename);
+               CG(in_compilation) = orig_in_compilation;
+       }
+}
+#endif
+
+zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC)
+{
+       zend_op_array *op_array;
+
+       op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
+       *op_array = persistent_script->main_op_array;
+
+       if (from_shared_memory) {
+               /* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
+               if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
+                       zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
+                       zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor TSRMLS_CC);
+                       zend_hash_destroy(&ZCG(bind_hash));
+               }
+               /* we must first to copy all classes and then prepare functions, since functions may try to bind
+                  classes - which depend on pre-bind class entries existant in the class table */
+               if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
+                       zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, (unique_copy_ctor_func_t)zend_prepare_function_for_execution);
+               }
+
+               zend_prepare_function_for_execution(op_array);
+
+               /* Register __COMPILER_HALT_OFFSET__ constant */
+               if (persistent_script->compiler_halt_offset != 0 &&
+                   persistent_script->full_path) {
+                       char *name, *cfilename;
+                       char haltoff[] = "__COMPILER_HALT_OFFSET__";
+                       int len, clen;
+
+                       cfilename = persistent_script->full_path;
+                       clen = strlen(cfilename);
+                       zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0);
+                       if (!zend_hash_exists(EG(zend_constants), name, len+1)) {
+                               zend_register_long_constant(name, len+1, persistent_script->compiler_halt_offset, CONST_CS, 0 TSRMLS_CC);
+                       }
+                       efree(name);
+               }
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+               if ((int)persistent_script->early_binding != -1) {
+                       zend_do_delayed_early_binding(op_array, persistent_script->early_binding TSRMLS_CC);
+               }
+#endif
+
+       } else /* if (!from_shared_memory) */ {
+               if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
+                       zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, NULL);
+               }
+               if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
+                       zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL TSRMLS_CC);
+               }
+               free_persistent_script(persistent_script, 0); /* free only hashes */
+       }
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       if (op_array->early_binding != (zend_uint)-1) {
+               char *orig_compiled_filename = CG(compiled_filename);
+               CG(compiled_filename) = persistent_script->full_path;
+               zend_do_delayed_early_binding(op_array TSRMLS_CC);
+               CG(compiled_filename) = orig_compiled_filename;
+       }
+#endif
+
+       return op_array;
+}
+
+/*
+ * zend_adler32() is based on zlib implementation
+ * Computes the Adler-32 checksum of a data stream
+ *
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
+ *
+ *  This software is provided 'as-is', without any express or implied
+ *  warranty.  In no event will the authors be held liable for any damages
+ *  arising from the use of this software.
+ *
+ *  Permission is granted to anyone to use this software for any purpose,
+ *  including commercial applications, and to alter it and redistribute it
+ *  freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software
+ *     in a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *  3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+#define ADLER32_BASE 65521 /* largest prime smaller than 65536 */
+#define ADLER32_NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define ADLER32_DO1(buf)    {s1 += *(buf); s2 += s1;}
+#define ADLER32_DO2(buf,i)  ADLER32_DO1(buf+i); ADLER32_DO1(buf+i+1);
+#define ADLER32_DO4(buf,i)  ADLER32_DO2(buf,i); ADLER32_DO2(buf,i+2);
+#define ADLER32_DO8(buf,i)  ADLER32_DO4(buf,i); ADLER32_DO4(buf,i+4);
+#define ADLER32_DO16(buf)   ADLER32_DO8(buf,0); ADLER32_DO8(buf,8);
+
+unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len)
+{
+       unsigned int s1 = checksum & 0xffff;
+       unsigned int s2 = (checksum >> 16) & 0xffff;
+       signed char *end;
+
+       while (len >= ADLER32_NMAX) {
+               len -= ADLER32_NMAX;
+               end = buf + ADLER32_NMAX;
+               do {
+                       ADLER32_DO16(buf);
+                       buf += 16;
+               } while (buf != end);
+               s1 %= ADLER32_BASE;
+               s2 %= ADLER32_BASE;
+       }
+
+       if (len) {
+               if (len >= 16) {
+                       end = buf + (len & 0xfff0);
+                       len &= 0xf;
+                       do {
+                               ADLER32_DO16(buf);
+                               buf += 16;
+                       } while (buf != end);
+               }
+               if (len) {
+                       end = buf + len;
+                       do {
+                               ADLER32_DO1(buf);
+                               buf++;
+                       } while (buf != end);
+               }
+               s1 %= ADLER32_BASE;
+               s2 %= ADLER32_BASE;
+    }
+
+       return (s2 << 16) | s1;
+}
diff --git a/zend_accelerator_util_funcs.h b/zend_accelerator_util_funcs.h
new file mode 100644 (file)
index 0000000..804e0f7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_UTIL_FUNCS_H
+#define ZEND_ACCELERATOR_UTIL_FUNCS_H
+
+#include "zend.h"
+#include "ZendAccelerator.h"
+
+void zend_accel_copy_internal_functions(TSRMLS_D);
+
+zend_persistent_script* create_persistent_script(void);
+void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements);
+
+void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC);
+void zend_accel_move_user_functions(HashTable *str, HashTable *dst TSRMLS_DC);
+
+zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC);
+
+#define ADLER32_INIT 1     /* initial Adler-32 value */
+
+unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len);
+
+#endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/zend_persist.c b/zend_persist.c
new file mode 100644 (file)
index 0000000..44a9809
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "ZendAccelerator.h"
+#include "zend_persist.h"
+#include "zend_extensions.h"
+#include "zend_shared_alloc.h"
+#include "zend_vm.h"
+#include "zend_constants.h"
+#include "zend_operators.h"
+
+#define zend_accel_store(p, size) \
+           (p = _zend_shared_memdup((void*)p, size, 1 TSRMLS_CC))
+#define zend_accel_memdup(p, size) \
+           _zend_shared_memdup((void*)p, size, 0 TSRMLS_CC)
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+# define zend_accel_memdup_interned_string(str, len) \
+       IS_INTERNED(str) ? str : zend_accel_memdup(str, len)
+
+# define zend_accel_store_interned_string(str, len) do { \
+               if (!IS_INTERNED(str)) { zend_accel_store(str, len); } \
+       } while (0)
+#else
+# define zend_accel_memdup_interned_string(str, len) \
+       zend_accel_memdup(str, len)
+
+# define zend_accel_store_interned_string(str, len) \
+       zend_accel_store(str, len)
+#endif
+
+typedef void (*zend_persist_func_t)(void * TSRMLS_DC);
+
+static void zend_persist_zval_ptr(zval **zp TSRMLS_DC);
+
+static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC)
+{
+       Bucket *p = ht->pListHead;
+       uint i;
+
+       while (p) {
+               Bucket *q = p;
+
+               /* persist bucket and key */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               p = zend_accel_memdup(p, sizeof(Bucket));
+               if (p->nKeyLength) {
+                       p->arKey = zend_accel_memdup_interned_string(p->arKey, p->nKeyLength);
+               }
+#else
+               p = zend_accel_memdup(p, sizeof(Bucket) - 1 + p->nKeyLength);
+#endif
+
+               /* persist data pointer in bucket */
+               if (!p->pDataPtr) {
+                       zend_accel_store(p->pData, el_size);
+               } else {
+                       /* Update p->pData to point to the new p->pDataPtr address, after the bucket relocation */
+                       p->pData = &p->pDataPtr;
+               }
+
+               /* persist the data itself */
+               if (pPersistElement) {
+                       pPersistElement(p->pData TSRMLS_CC);
+               }
+
+               /* update linked lists */
+               if (p->pLast) {
+                       p->pLast->pNext = p;
+               }
+               if (p->pNext) {
+                       p->pNext->pLast = p;
+               }
+               if (p->pListLast) {
+                       p->pListLast->pListNext = p;
+               }
+               if (p->pListNext) {
+                       p->pListNext->pListLast = p;
+               }
+
+               p = p->pListNext;
+
+               /* delete the old non-persistent bucket */
+               efree(q);
+       }
+
+       /* update linked lists */
+       if (ht->pListHead) {
+               ht->pListHead = zend_shared_alloc_get_xlat_entry(ht->pListHead);
+       }
+       if (ht->pListTail) {
+               ht->pListTail = zend_shared_alloc_get_xlat_entry(ht->pListTail);
+       }
+       if (ht->pInternalPointer) {
+               ht->pInternalPointer = zend_shared_alloc_get_xlat_entry(ht->pInternalPointer);
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       /* Check if HastTable is initialized */
+       if (ht->nTableMask) {
+#endif
+               if (ht->nNumOfElements) {
+                       /* update hash table */
+                       for (i = 0; i < ht->nTableSize; i++) {
+                               if (ht->arBuckets[i]) {
+                                       ht->arBuckets[i] = zend_shared_alloc_get_xlat_entry(ht->arBuckets[i]);
+                               }
+                       }
+               }
+               zend_accel_store(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       } else {
+               ht->arBuckets = NULL;
+       }
+#endif
+}
+
+static void zend_persist_zval(zval *z TSRMLS_DC)
+{
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       switch (z->type & IS_CONSTANT_TYPE_MASK) {
+#else
+       switch (z->type & ~IS_CONSTANT_INDEX) {
+#endif
+               case IS_STRING:
+               case IS_CONSTANT:
+                       zend_accel_store_interned_string(z->value.str.val, z->value.str.len + 1);
+                       break;
+               case IS_ARRAY:
+               case IS_CONSTANT_ARRAY:
+                       zend_accel_store(z->value.ht, sizeof(HashTable));
+                       zend_hash_persist(z->value.ht, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
+                       break;
+       }
+}
+
+static void zend_persist_zval_ptr(zval **zp TSRMLS_DC)
+{
+       zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp);
+
+       if (new_ptr) {
+               *zp = new_ptr;
+       } else {
+               /* Attempt to store only if we didn't store this zval_ptr yet */
+               zend_accel_store(*zp, sizeof(zval));
+               zend_persist_zval(*zp TSRMLS_CC);
+       }
+}
+
+static void zend_protect_zval(zval *z TSRMLS_DC)
+{
+       PZ_SET_ISREF_P(z);
+       PZ_SET_REFCOUNT_P(z,2);
+}
+
+static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script TSRMLS_DC)
+{
+       zend_op *persist_ptr;
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+       int has_jmp = 0;
+#endif
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       zend_literal *orig_literals = NULL;
+#endif
+
+       if (op_array->type != ZEND_USER_FUNCTION) {
+               return;
+       }
+
+#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO
+       op_array->size = op_array->last;
+#endif
+
+       if (--(*op_array->refcount) == 0) {
+               efree(op_array->refcount);
+       }
+       op_array->refcount = NULL;
+
+       if (op_array->filename) {
+               /* do not free! PHP has centralized filename storage, compiler will free it */
+               op_array->filename = zend_accel_memdup(op_array->filename, strlen(op_array->filename) + 1);
+       }
+
+       if (main_persistent_script) {
+               zend_bool orig_in_execution = EG(in_execution);
+               zend_op_array *orig_op_array = EG(active_op_array);
+               zval offset;
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+               main_persistent_script->early_binding = -1;
+#endif
+               EG(in_execution) = 1;
+               EG(active_op_array) = op_array;
+               if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) {
+                       main_persistent_script->compiler_halt_offset = Z_LVAL(offset);
+               }
+               EG(active_op_array) = orig_op_array;
+               EG(in_execution) = orig_in_execution;
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (op_array->literals) {
+               orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals);
+               if (orig_literals) {
+                       op_array->literals = orig_literals;
+               } else {
+                       zend_literal *p = zend_accel_memdup(op_array->literals, sizeof(zend_literal) * op_array->last_literal);
+                       zend_literal *end = p + op_array->last_literal;
+                       orig_literals = op_array->literals;
+                       op_array->literals = p;
+                       while (p < end) {
+                               zend_persist_zval(&p->constant TSRMLS_CC);
+                               zend_protect_zval(&p->constant TSRMLS_CC);
+                               p++;
+                       }
+                       efree(orig_literals);
+               }
+       }
+#endif
+
+       if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
+               op_array->opcodes = persist_ptr;
+       } else {
+               zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last);
+               zend_op *opline = new_opcodes;
+               zend_op *end = new_opcodes + op_array->last;
+               int offset = 0;
+
+               for (;opline<end;opline++, offset++) {
+                       if (ZEND_OP1_TYPE(opline)==IS_CONST) {
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals));
+#else
+                               zend_persist_zval(&opline->op1.u.constant TSRMLS_CC);
+                               zend_protect_zval(&opline->op1.u.constant TSRMLS_CC);
+#endif
+                       }
+                       if (ZEND_OP2_TYPE(opline)==IS_CONST) {
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                               opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals));
+#else
+                               zend_persist_zval(&opline->op2.u.constant TSRMLS_CC);
+                               zend_protect_zval(&opline->op2.u.constant TSRMLS_CC);
+#endif
+                       }
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+                       switch (opline->opcode) {
+                               case ZEND_JMP:
+                                       has_jmp = 1;
+                                       if (ZEND_DONE_PASS_TWO(op_array)) {
+                                               ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
+                                       }
+                                       break;
+                               case ZEND_JMPZ:
+                               case ZEND_JMPNZ:
+                               case ZEND_JMPZ_EX:
+                               case ZEND_JMPNZ_EX:
+                                       has_jmp = 1;
+                                       if (ZEND_DONE_PASS_TWO(op_array)) {
+                                               ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
+                                       }
+                                       break;
+                               case ZEND_JMPZNZ:
+                               case ZEND_BRK:
+                               case ZEND_CONT:
+                                       has_jmp = 1;
+                                       break;
+                               case ZEND_DECLARE_INHERITED_CLASS:
+                                       if (main_persistent_script && ZCG(accel_directives).inherited_hack) {
+                                       if (!has_jmp &&
+                                          ((opline+2) >= end ||
+                                           (opline+1)->opcode != ZEND_FETCH_CLASS ||
+                                           (opline+2)->opcode != ZEND_ADD_INTERFACE)) {
+
+                                               zend_uint *opline_num = &main_persistent_script->early_binding;
+
+                                               while ((int)*opline_num != -1) {
+                                                       opline_num = &new_opcodes[*opline_num].result.u.opline_num;
+                                               }
+                                               *opline_num = opline - new_opcodes;
+                                               opline->result.op_type = IS_UNUSED;
+                                               opline->result.u.opline_num = -1;
+                                               opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;
+                                               ZEND_VM_SET_OPCODE_HANDLER(opline);
+                                       }
+                                       break;
+                               }
+                       }
+
+#else /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */
+
+                       if (ZEND_DONE_PASS_TWO(op_array)) {
+                               /* fix jmps to point to new array */
+                               switch (opline->opcode) {
+                                       case ZEND_JMP:
+                                       case ZEND_GOTO:
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+                                       case ZEND_FAST_CALL:
+#endif
+                                               ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
+                                               break;
+                                       case ZEND_JMPZ:
+                                       case ZEND_JMPNZ:
+                                       case ZEND_JMPZ_EX:
+                                       case ZEND_JMPNZ_EX:
+                                       case ZEND_JMP_SET:
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                                       case ZEND_JMP_SET_VAR:
+#endif
+                                               ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
+                                               break;
+                               }
+                       }
+#endif /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */ 
+               }
+
+               efree(op_array->opcodes);
+               op_array->opcodes = new_opcodes;
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (op_array->run_time_cache) {
+                       efree(op_array->run_time_cache);
+                       op_array->run_time_cache = NULL;
+               }
+#endif
+       }
+
+       if (op_array->function_name) {
+               char *new_name;
+               if ((new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name))) {
+                       op_array->function_name = new_name;
+               } else {
+                       zend_accel_store(op_array->function_name, strlen(op_array->function_name)+1);
+               }
+       }
+
+       if (op_array->arg_info) {
+               zend_arg_info *new_ptr;
+               if((new_ptr = zend_shared_alloc_get_xlat_entry(op_array->arg_info))) {
+                       op_array->arg_info = new_ptr;
+               } else {
+                       zend_uint i;
+
+                       zend_accel_store(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args);
+                       for(i=0;i<op_array->num_args;i++) {
+                               if(op_array->arg_info[i].name) {
+                                       zend_accel_store_interned_string(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1);
+                               }
+                               if(op_array->arg_info[i].class_name) {
+                                       zend_accel_store_interned_string(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1);
+                               }
+                       }
+               }
+       }
+
+       if (op_array->brk_cont_array) {
+               zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+       }
+
+       if (op_array->static_variables) {
+               zend_hash_persist(op_array->static_variables, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
+               zend_accel_store(op_array->static_variables, sizeof(HashTable));
+       }
+
+       if(op_array->scope) {
+               op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
+       }
+
+       if(op_array->doc_comment) {
+               if (ZCG(accel_directives).save_comments) {
+                       zend_accel_store(op_array->doc_comment, op_array->doc_comment_len + 1);
+               } else {
+                       if(!zend_shared_alloc_get_xlat_entry(op_array->doc_comment)) {
+                               zend_shared_alloc_register_xlat_entry(op_array->doc_comment, op_array->doc_comment);
+                               efree((char*)op_array->doc_comment);
+                       }
+                       op_array->doc_comment = NULL;
+                       op_array->doc_comment_len = 0;
+               }
+       }
+
+       if(op_array->try_catch_array) {
+               zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
+       }
+
+       if(op_array->vars) {
+               if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars))) {
+                       op_array->vars = (zend_compiled_variable*)persist_ptr;
+               } else {
+                       int i;
+                       zend_accel_store(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var);
+                       for(i=0; i<op_array->last_var; i++) {
+                               zend_accel_store_interned_string(op_array->vars[i].name, op_array->vars[i].name_len + 1);
+                       }
+               }
+       }
+
+       /* "prototype" may be undefined if "scope" isn't set */
+       if(op_array->scope && op_array->prototype) {
+               if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) {
+                       op_array->prototype = (union _zend_function*)persist_ptr;
+                       /* we use refcount to show that op_array is referenced from several places */
+                       op_array->prototype->op_array.refcount++;
+               }
+       } else {
+               op_array->prototype = NULL;
+       }
+}
+
+static void zend_persist_op_array(zend_op_array *op_array TSRMLS_DC)
+{
+       zend_persist_op_array_ex(op_array, NULL TSRMLS_CC);
+}
+
+static void zend_persist_property_info(zend_property_info *prop TSRMLS_DC)
+{
+       zend_accel_store_interned_string(prop->name, prop->name_length + 1);
+       if(prop->doc_comment) {
+               if (ZCG(accel_directives).save_comments) {
+                       zend_accel_store(prop->doc_comment, prop->doc_comment_len + 1);
+               } else {
+                       if(!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) {
+                               zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment);
+                               efree((char*)prop->doc_comment);
+                       }
+                       prop->doc_comment = NULL;
+                       prop->doc_comment_len = 0;
+               }
+       }
+}
+
+static void zend_persist_class_entry(zend_class_entry **pce TSRMLS_DC)
+{
+       zend_class_entry *ce = *pce;
+
+       if (ce->type == ZEND_USER_CLASS) {
+               *pce = zend_accel_store(ce, sizeof(zend_class_entry));
+               zend_accel_store_interned_string(ce->name, ce->name_length + 1);
+               zend_hash_persist(&ce->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC);
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (ce->default_properties_table) {
+                   int i;
+
+                       zend_accel_store(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count);
+                       for (i = 0; i < ce->default_properties_count; i++) {
+                               if (ce->default_properties_table[i]) {
+                                       zend_persist_zval_ptr(&ce->default_properties_table[i] TSRMLS_CC);
+                               }
+                       }
+               }
+               if (ce->default_static_members_table) {
+                   int i;
+
+                       zend_accel_store(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count);
+                       for (i = 0; i < ce->default_static_members_count; i++) {
+                               if (ce->default_static_members_table[i]) {
+                                       zend_persist_zval_ptr(&ce->default_static_members_table[i] TSRMLS_CC);
+                               }
+                       }
+               }
+               ce->static_members_table = NULL;
+#else
+               zend_hash_persist(&ce->default_properties, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
+               zend_hash_persist(&ce->default_static_members, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
+               ce->static_members = NULL;
+#endif
+               zend_hash_persist(&ce->constants_table, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
+
+               if(ZEND_CE_FILENAME(ce)) {
+                       /* do not free! PHP has centralized filename storage, compiler will free it */
+                       ZEND_CE_FILENAME(ce) = zend_accel_memdup(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1);
+               }
+               if(ZEND_CE_DOC_COMMENT(ce)) {
+                       if (ZCG(accel_directives).save_comments) {
+                               zend_accel_store(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1);
+                       } else {
+                               if(!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) {
+                                       zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce));
+                                       efree((char*)ZEND_CE_DOC_COMMENT(ce));
+                               }
+                               ZEND_CE_DOC_COMMENT(ce) = NULL;
+                               ZEND_CE_DOC_COMMENT_LEN(ce) = 0;
+                       }
+               }
+               zend_hash_persist(&ce->properties_info, (zend_persist_func_t) zend_persist_property_info, sizeof(zend_property_info) TSRMLS_CC);
+               if(ce->num_interfaces && ce->interfaces) {
+                       efree(ce->interfaces);
+               }
+               ce->interfaces = NULL; /* will be filled in on fetch */
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (ce->num_traits && ce->traits) {
+                       efree(ce->traits);
+               }
+               ce->traits = NULL;
+
+               if (ce->trait_aliases) {
+                       int i = 0;
+                       while (ce->trait_aliases[i]) {
+                               if (ce->trait_aliases[i]->trait_method) {
+                                       if (ce->trait_aliases[i]->trait_method->method_name) {
+                                               zend_accel_store(ce->trait_aliases[i]->trait_method->method_name,
+                                                       ce->trait_aliases[i]->trait_method->mname_len+1);
+                                       }
+                                       if (ce->trait_aliases[i]->trait_method->class_name) {
+                                               zend_accel_store(ce->trait_aliases[i]->trait_method->class_name,
+                                                       ce->trait_aliases[i]->trait_method->cname_len+1);
+                                       }
+                                       ce->trait_aliases[i]->trait_method->ce = NULL;
+                                       zend_accel_store(ce->trait_aliases[i]->trait_method,
+                                               sizeof(zend_trait_method_reference));
+                               }
+
+                               if (ce->trait_aliases[i]->alias) {
+                                       zend_accel_store(ce->trait_aliases[i]->alias,
+                                               ce->trait_aliases[i]->alias_len+1);
+                               }
+
+#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO
+                ce->trait_aliases[i]->function = NULL;
+#endif
+                               zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias));
+                               i++;
+                       }
+
+                       zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1));
+               }
+
+               if (ce->trait_precedences) {
+                       int i = 0;
+
+                       while (ce->trait_precedences[i]) {
+                               zend_accel_store(ce->trait_precedences[i]->trait_method->method_name,
+                                       ce->trait_precedences[i]->trait_method->mname_len+1);
+                               zend_accel_store(ce->trait_precedences[i]->trait_method->class_name,
+                                       ce->trait_precedences[i]->trait_method->cname_len+1);
+                               ce->trait_precedences[i]->trait_method->ce = NULL;
+                               zend_accel_store(ce->trait_precedences[i]->trait_method,
+                                       sizeof(zend_trait_method_reference));
+
+                               if (ce->trait_precedences[i]->exclude_from_classes) {
+                                       int j = 0;
+
+                                       while (ce->trait_precedences[i]->exclude_from_classes[j]) {
+                                               zend_accel_store(ce->trait_precedences[i]->exclude_from_classes[j],
+                                                       strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1);
+                                               j++;
+                                       }
+                                       zend_accel_store(ce->trait_precedences[i]->exclude_from_classes,
+                                               sizeof(zend_class_entry*) * (j+1));
+                               }
+
+#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO
+                ce->trait_precedences[i]->function = NULL;
+#endif
+                               zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence));
+                               i++;
+                       }
+                       zend_accel_store(
+                               ce->trait_precedences, sizeof(zend_trait_precedence*) * (i+1));
+               }
+#endif
+       }
+}
+
+static int zend_update_property_info_ce(zend_property_info *prop TSRMLS_DC)
+{
+       prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
+       return 0;
+}
+
+static int zend_update_parent_ce(zend_class_entry **pce TSRMLS_DC)
+{
+       zend_class_entry *ce = *pce;
+
+       if (ce->parent) {
+               ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent);
+               /* We use refcount to show if the class is used as a parent */
+               ce->parent->refcount++;
+       }
+
+       /* update methods */
+       if(ce->constructor) {
+               ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor);
+               /* we use refcount to show that op_array is referenced from several places */
+               ce->constructor->op_array.refcount++;
+       }
+       if(ce->destructor) {
+               ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor);
+               ce->destructor->op_array.refcount++;
+       }
+       if(ce->clone) {
+               ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone);
+               ce->clone->op_array.refcount++;
+       }
+       if(ce->__get) {
+               ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get);
+               ce->__get->op_array.refcount++;
+       }
+       if(ce->__set) {
+               ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set);
+               ce->__set->op_array.refcount++;
+       }
+       if(ce->__call) {
+               ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call);
+               ce->__call->op_array.refcount++;
+       }
+       if(ce->serialize_func) {
+               ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func);
+               ce->serialize_func->op_array.refcount++;
+       }
+       if(ce->unserialize_func) {
+               ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func);
+               ce->unserialize_func->op_array.refcount++;
+       }
+       if(ce->__isset) {
+               ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset);
+               ce->__isset->op_array.refcount++;
+       }
+       if(ce->__unset) {
+               ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset);
+               ce->__unset->op_array.refcount++;
+       }
+       if(ce->__tostring) {
+               ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring);
+               ce->__tostring->op_array.refcount++;
+       }
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       if(ce->__callstatic) {
+               ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic);
+               ce->__callstatic->op_array.refcount++;
+       }
+#endif
+       zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce TSRMLS_CC);
+       return 0;
+}
+
+static void zend_accel_persist_class_table(HashTable *class_table TSRMLS_DC)
+{
+    zend_hash_persist(class_table, (zend_persist_func_t) zend_persist_class_entry, sizeof(zend_class_entry*) TSRMLS_CC);
+       zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce TSRMLS_CC);
+}
+
+zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC)
+{
+       zend_shared_alloc_clear_xlat_table();
+       zend_hash_persist(&script->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC);
+       zend_accel_persist_class_table(&script->class_table TSRMLS_CC);
+       zend_persist_op_array_ex(&script->main_op_array, script TSRMLS_CC);
+       *key = zend_accel_memdup(*key, key_length + 1);
+       zend_accel_store(script->full_path, script->full_path_len + 1);
+       zend_accel_store(script, sizeof(zend_persistent_script));
+
+       return script;
+}
+
+int zend_accel_script_persistable(zend_persistent_script *script)
+{
+       return 1;
+}
diff --git a/zend_persist.h b/zend_persist.h
new file mode 100644 (file)
index 0000000..ffb990a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_PERSIST_H
+#define ZEND_PERSIST_H
+
+int zend_accel_script_persistable(zend_persistent_script *script);
+uint zend_accel_script_persist_calc(zend_persistent_script *script, char *key, unsigned int key_length TSRMLS_DC);
+zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC);
+
+#endif /* ZEND_PERSIST_H */
diff --git a/zend_persist_calc.c b/zend_persist_calc.c
new file mode 100644 (file)
index 0000000..36ec05b
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "ZendAccelerator.h"
+#include "zend_persist.h"
+#include "zend_extensions.h"
+#include "zend_shared_alloc.h"
+#include "zend_operators.h"
+
+#define START_SIZE()       uint memory_used = 0
+#define ADD_DUP_SIZE(m,s)  memory_used += zend_shared_memdup_size((void*)m, s)
+#define ADD_SIZE(m)        memory_used += ZEND_ALIGNED_SIZE(m)
+#define RETURN_SIZE()      return memory_used
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+# define ADD_INTERNED_STRING(str, len) do { \
+               const char *tmp = accel_new_interned_string((str), (len), !IS_INTERNED((str)) TSRMLS_CC); \
+               if (tmp != (str)) { \
+                       (str) = (char*)tmp; \
+               } else { \
+                       ADD_DUP_SIZE((str), (len)); \
+               } \
+       } while (0)
+#else
+# define ADD_INTERNED_STRING(str, len) ADD_DUP_SIZE((str), (len))
+#endif
+
+static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC);
+
+static uint zend_hash_persist_calc(HashTable *ht, int (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC)
+{
+       Bucket *p = ht->pListHead;
+       START_SIZE();
+
+       while (p) {
+               /* persist bucket and key */
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               ADD_DUP_SIZE(p, sizeof(Bucket));
+               if (p->nKeyLength) {
+                       const char *tmp = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
+                       if (tmp != p->arKey) {
+                               p->arKey = tmp;
+                       } else {
+                               ADD_DUP_SIZE(p->arKey, p->nKeyLength);
+                       }
+               }
+#else
+               ADD_DUP_SIZE(p, sizeof(Bucket) - 1 + p->nKeyLength);
+#endif
+
+               /* persist data pointer in bucket */
+               if (!p->pDataPtr) {
+                       ADD_DUP_SIZE(p->pData, el_size);
+               }
+
+               /* persist the data itself */
+               if (pPersistElement) {
+                       ADD_SIZE(pPersistElement(p->pData TSRMLS_CC));
+               }
+
+               p = p->pListNext;
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (ht->nTableMask) {
+               ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
+       }
+#else
+       ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
+#endif
+
+       RETURN_SIZE();
+}
+
+static uint zend_persist_zval_calc(zval *z TSRMLS_DC)
+{
+       START_SIZE();
+
+#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+       switch (z->type & IS_CONSTANT_TYPE_MASK) {
+#else
+       switch (z->type & ~IS_CONSTANT_INDEX) {
+#endif
+               case IS_STRING:
+               case IS_CONSTANT:
+                       ADD_INTERNED_STRING(Z_STRVAL_P(z), Z_STRLEN_P(z) + 1);
+                       break;
+               case IS_ARRAY:
+               case IS_CONSTANT_ARRAY:
+                       ADD_DUP_SIZE(z->value.ht, sizeof(HashTable));
+                       ADD_SIZE(zend_hash_persist_calc(z->value.ht, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
+                       break;
+       }
+       RETURN_SIZE();
+}
+
+static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC)
+{
+       START_SIZE();
+       zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp);
+
+       if (!new_ptr) {
+               ADD_DUP_SIZE(*zp, sizeof(zval));
+               ADD_SIZE(zend_persist_zval_calc(*zp TSRMLS_CC));
+       }
+       RETURN_SIZE();
+}
+
+static uint zend_persist_op_array_calc(zend_op_array *op_array TSRMLS_DC)
+{
+       START_SIZE();
+
+       if (op_array->type != ZEND_USER_FUNCTION) {
+               return 0;
+       }
+
+       if (op_array->filename) {
+               ADD_DUP_SIZE(op_array->filename, strlen(op_array->filename) + 1);
+       }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+       if (op_array->literals && !zend_shared_alloc_get_xlat_entry(op_array->literals)) {
+               zend_literal *p = op_array->literals;
+               zend_literal *end = p + op_array->last_literal;
+               ADD_DUP_SIZE(op_array->literals, sizeof(zend_literal) * op_array->last_literal);
+               while (p < end) {
+                       ADD_SIZE(zend_persist_zval_calc(&p->constant TSRMLS_CC));
+                       p++;
+               }
+       }
+#endif
+
+       if (!zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
+#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO
+               zend_op *opline = op_array->opcodes;
+               zend_op *end = op_array->opcodes+op_array->last;
+
+               ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last);
+               while (opline<end) {
+                       if (opline->op1.op_type==IS_CONST) {
+                               ADD_SIZE(zend_persist_zval_calc(&opline->op1.u.constant TSRMLS_CC));
+                       }
+                       if (opline->op2.op_type==IS_CONST) {
+                               ADD_SIZE(zend_persist_zval_calc(&opline->op2.u.constant TSRMLS_CC));
+                       }
+                       opline++;
+               }
+#else
+               ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last);
+#endif
+       }
+
+       if (op_array->function_name) {
+               ADD_DUP_SIZE(op_array->function_name, strlen(op_array->function_name) + 1);
+    }
+
+       if (op_array->arg_info &&
+               !zend_shared_alloc_get_xlat_entry(op_array->arg_info)) {
+               zend_uint i;
+
+               ADD_DUP_SIZE(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args);
+               for(i=0;i<op_array->num_args;i++) {
+                       if(op_array->arg_info[i].name) {
+                               ADD_INTERNED_STRING(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1);
+                       }
+                       if(op_array->arg_info[i].class_name) {
+                               ADD_INTERNED_STRING(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1);
+                       }
+
+               }
+       }
+
+       if (op_array->brk_cont_array) {
+               ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+       }
+
+       if (op_array->static_variables) {
+               ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable));
+               ADD_SIZE(zend_hash_persist_calc(op_array->static_variables, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
+       }
+
+       if(ZCG(accel_directives).save_comments && op_array->doc_comment) {
+               ADD_DUP_SIZE(op_array->doc_comment, op_array->doc_comment_len + 1);
+       }
+
+       if(op_array->try_catch_array) {
+               ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
+       }
+
+       if(op_array->vars && !zend_shared_alloc_get_xlat_entry(op_array->vars)) {
+               int i;
+
+               ADD_DUP_SIZE(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var);
+               for(i=0; i<op_array->last_var; i++) {
+                       ADD_INTERNED_STRING(op_array->vars[i].name, op_array->vars[i].name_len + 1);
+               }
+       }
+
+       RETURN_SIZE();
+}
+
+static uint zend_persist_property_info_calc(zend_property_info *prop TSRMLS_DC)
+{
+       START_SIZE();
+       ADD_INTERNED_STRING(prop->name, prop->name_length + 1);
+       if(ZCG(accel_directives).save_comments && prop->doc_comment) {
+               ADD_DUP_SIZE(prop->doc_comment, prop->doc_comment_len + 1);
+       }
+       RETURN_SIZE();
+}
+
+static uint zend_persist_class_entry_calc(zend_class_entry **pce TSRMLS_DC)
+{
+       zend_class_entry *ce = *pce;
+       START_SIZE();
+
+       if (ce->type == ZEND_USER_CLASS) {
+               ADD_DUP_SIZE(ce, sizeof(zend_class_entry));
+               ADD_INTERNED_STRING(ce->name, ce->name_length + 1);
+               ADD_SIZE(zend_hash_persist_calc(&ce->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC));
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (ce->default_properties_table) {
+                   int i;
+
+                       ADD_SIZE(sizeof(zval*) * ce->default_properties_count);
+                       for (i = 0; i < ce->default_properties_count; i++) {
+                               if (ce->default_properties_table[i]) {
+                                       ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_properties_table[i] TSRMLS_CC));
+                               }
+                       }
+               }
+               if (ce->default_static_members_table) {
+                   int i;
+
+                       ADD_SIZE(sizeof(zval*) * ce->default_static_members_count);
+                       for (i = 0; i < ce->default_static_members_count; i++) {
+                               if (ce->default_static_members_table[i]) {
+                                       ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_static_members_table[i] TSRMLS_CC));
+                               }
+                       }
+               }
+#else
+               ADD_SIZE(zend_hash_persist_calc(&ce->default_properties, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
+               ADD_SIZE(zend_hash_persist_calc(&ce->default_static_members, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
+#endif
+               ADD_SIZE(zend_hash_persist_calc(&ce->constants_table, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
+
+               if (ZEND_CE_FILENAME(ce)) {
+                       ADD_DUP_SIZE(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1);
+               }
+               if(ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) {
+                       ADD_DUP_SIZE(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1);
+               }
+
+               ADD_SIZE(zend_hash_persist_calc(&ce->properties_info, (int (*)(void* TSRMLS_DC)) zend_persist_property_info_calc, sizeof(zend_property_info) TSRMLS_CC));
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+               if (ce->trait_aliases) {
+                       int i = 0;
+                       while (ce->trait_aliases[i]) {
+                               if (ce->trait_aliases[i]->trait_method) {
+                                       if (ce->trait_aliases[i]->trait_method->method_name) {
+                                               ADD_SIZE(ce->trait_aliases[i]->trait_method->mname_len+1);
+                                       }
+                                       if (ce->trait_aliases[i]->trait_method->class_name) {
+                                               ADD_SIZE(ce->trait_aliases[i]->trait_method->cname_len+1);
+                                       }
+                                       ADD_SIZE(sizeof(zend_trait_method_reference));
+                               }
+
+                               if (ce->trait_aliases[i]->alias) {
+                                       ADD_SIZE(ce->trait_aliases[i]->alias_len+1);
+                               }
+                               ADD_SIZE(sizeof(zend_trait_alias));
+                               i++;
+                       }
+                       ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1));
+               }
+
+               if (ce->trait_precedences) {
+                       int i = 0;
+
+                       while (ce->trait_precedences[i]) {
+                               ADD_SIZE(ce->trait_precedences[i]->trait_method->mname_len+1);
+                               ADD_SIZE(ce->trait_precedences[i]->trait_method->cname_len+1);
+                               ADD_SIZE(sizeof(zend_trait_method_reference));
+
+                               if (ce->trait_precedences[i]->exclude_from_classes) {
+                                       int j = 0;
+
+                                       while (ce->trait_precedences[i]->exclude_from_classes[j]) {
+                                               ADD_SIZE(strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1);
+                                               j++;
+                                       }
+                                       ADD_SIZE(sizeof(zend_class_entry*) * (j+1));
+                               }
+                               ADD_SIZE(sizeof(zend_trait_precedence));
+                               i++;
+                       }
+                       ADD_SIZE(sizeof(zend_trait_precedence*) * (i+1));
+               }
+#endif
+       }
+       RETURN_SIZE();
+}
+
+static uint zend_accel_persist_class_table_calc(HashTable *class_table TSRMLS_DC)
+{
+       return zend_hash_persist_calc(class_table, (int (*)(void* TSRMLS_DC)) zend_persist_class_entry_calc, sizeof(zend_class_entry*) TSRMLS_CC);
+}
+
+uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length TSRMLS_DC)
+{
+       START_SIZE();
+
+       ADD_SIZE(zend_hash_persist_calc(&new_persistent_script->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC));
+       ADD_SIZE(zend_accel_persist_class_table_calc(&new_persistent_script->class_table TSRMLS_CC));
+       ADD_SIZE(zend_persist_op_array_calc(&new_persistent_script->main_op_array TSRMLS_CC));
+       ADD_DUP_SIZE(key, key_length + 1);
+       ADD_DUP_SIZE(new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
+       ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script));
+
+       RETURN_SIZE();
+}
diff --git a/zend_shared_alloc.c b/zend_shared_alloc.c
new file mode 100644 (file)
index 0000000..c0024c6
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include <errno.h>
+#include "ZendAccelerator.h"
+#include "zend_shared_alloc.h"
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#ifndef ZEND_WIN32
+# include <sys/types.h>
+# include <dirent.h>
+# include <signal.h>
+# include <sys/stat.h>
+# include <stdio.h>
+#endif
+
+#ifdef HAVE_MPROTECT
+# include "sys/mman.h"
+#endif
+
+#define TMP_DIR "/tmp"
+#define SEM_FILENAME_PREFIX ".ZendSem."
+#define S_H(s) g_shared_alloc_handler->s
+
+/* True globals */
+static zend_bool locked;
+/* 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 */
+zend_smm_shared_globals *smm_shared_globals;
+
+#ifndef ZEND_WIN32
+int lock_file;
+static char lockfile_name[sizeof(TMP_DIR)+sizeof(SEM_FILENAME_PREFIX)+8];
+#endif
+
+static const zend_shared_memory_handler_entry handler_table[] = {
+#if USE_MMAP
+       { "mmap", &zend_alloc_mmap_handlers },
+#endif
+#if USE_SHM
+       { "shm", &zend_alloc_shm_handlers },
+#endif
+#if USE_SHM_OPEN
+       { "posix", &zend_alloc_posix_handlers },
+#endif
+#ifdef ZEND_WIN32
+       { "win32", &zend_alloc_win32_handlers },
+#endif
+       { NULL, NULL}
+};
+
+#ifndef ZEND_WIN32
+void zend_shared_alloc_create_lock(void)
+{
+       int val;
+
+       sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX);
+       lock_file = mkstemp(lockfile_name);
+       fchmod(lock_file, 0666);
+
+       if (lock_file == -1) {
+               zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
+       }
+       val = fcntl(lock_file, F_GETFD, 0);
+       val |= FD_CLOEXEC;
+       fcntl(lock_file, F_SETFD, val);
+
+       unlink(lockfile_name);
+}
+#endif
+
+static void no_memory_bailout(int allocate_size, char *error)
+{
+       zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %d bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
+}
+
+static void copy_shared_segments(void *to, void *from, int count, int size)
+{
+       zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
+       void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
+       void *shared_segments_from_p = from;
+       int i;
+
+       for(i=0;i<count;i++) {
+               shared_segments_v[i] =  shared_segments_to_p;
+               memcpy(shared_segments_to_p, shared_segments_from_p, size);
+               shared_segments_to_p = ((char *)shared_segments_to_p + size);
+               shared_segments_from_p = ((char *)shared_segments_from_p + size);
+       }
+}
+
+static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, int requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+       int res;
+       g_shared_alloc_handler = he->handler;
+       g_shared_model = he->name;
+       ZSMMG(shared_segments) = NULL;
+       ZSMMG(shared_segments_count) = 0;
+
+       res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
+
+       if( res ) {
+               /* this model works! */
+               return res;
+       }
+       if(*shared_segments_p) {
+               int i;
+               /* cleanup */
+               for(i=0;i<*shared_segments_count;i++) {
+                       if((*shared_segments_p)[i]->p && (int)(*shared_segments_p)[i]->p != -1) {
+                               S_H(detach_segment)((*shared_segments_p)[i]);
+                       }
+               }
+               free(*shared_segments_p);
+               *shared_segments_p = NULL;
+       }
+       g_shared_alloc_handler = NULL;
+       return ALLOC_FAILURE;
+}
+
+int zend_shared_alloc_startup(int requested_size)
+{
+       zend_shared_segment **tmp_shared_segments;
+       size_t shared_segments_array_size;
+       zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
+       char *error_in = NULL;
+       const zend_shared_memory_handler_entry *he;
+       int res = ALLOC_FAILURE;
+
+       TSRMLS_FETCH();
+
+       /* shared_free must be valid before we call zend_shared_alloc()
+        * - make it temporarily point to a local variable
+        */
+       smm_shared_globals = &tmp_shared_globals;
+       ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
+
+       zend_shared_alloc_create_lock();
+
+       if(ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
+               char* model = ZCG(accel_directives).memory_model;
+               /* "cgi" is really "shm"... */
+               if( strncmp(ZCG(accel_directives).memory_model,"cgi",4) == 0 ){
+                       model = "shm";
+               }
+
+               for(he = handler_table; he->name; he++) {
+                       if(strcmp(model, he->name) == 0) {
+                               res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
+                               if( res ) {
+                                       /* this model works! */
+                               }
+                               break;
+                       }
+               }
+       }
+
+       if( res == FAILED_REATTACHED ){
+               smm_shared_globals = NULL;
+               return res;
+       }
+
+       if(!g_shared_alloc_handler) {
+               /* try memory handlers in order */
+               for(he = handler_table; he->name; he++) {
+                       res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
+                       if( res ) {
+                               /* this model works! */
+                               break;
+                       }
+               }
+       }
+
+       if(!g_shared_alloc_handler) {
+               no_memory_bailout(requested_size, error_in);
+               return ALLOC_FAILURE;
+       }
+
+       if( res == SUCCESSFULLY_REATTACHED ){
+               return res;
+       }
+
+       shared_segments_array_size = ZSMMG(shared_segments_count)*S_H(segment_type_size)();
+
+       /* move shared_segments and shared_free to shared memory */
+       locked = 1; /* no need to perform a real lock at this point */
+       p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
+
+       tmp_shared_segments = zend_shared_alloc(shared_segments_array_size+ZSMMG(shared_segments_count)*sizeof(void *));
+       copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
+
+       *p_tmp_shared_globals = tmp_shared_globals;
+       smm_shared_globals = p_tmp_shared_globals;
+
+       free(ZSMMG(shared_segments));
+       ZSMMG(shared_segments) = tmp_shared_segments;
+
+       ZSMMG(shared_memory_state).positions = (int *) zend_shared_alloc(sizeof(int)*ZSMMG(shared_segments_count));
+       locked = 0;
+
+       return res;
+}
+
+void zend_shared_alloc_shutdown(void)
+{
+       zend_shared_segment **tmp_shared_segments;
+       size_t shared_segments_array_size;
+       zend_smm_shared_globals tmp_shared_globals;
+       int i;
+
+       tmp_shared_globals = *smm_shared_globals;
+       smm_shared_globals = &tmp_shared_globals;
+       shared_segments_array_size = ZSMMG(shared_segments_count)*(S_H(segment_type_size)()+sizeof(void *));
+       tmp_shared_segments = emalloc(shared_segments_array_size);
+       copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
+       ZSMMG(shared_segments) = tmp_shared_segments;
+
+       for(i=0; i<ZSMMG(shared_segments_count); i++) {
+               S_H(detach_segment)(ZSMMG(shared_segments)[i]);
+       }
+       efree(ZSMMG(shared_segments));
+       ZSMMG(shared_segments) = NULL;
+       g_shared_alloc_handler = NULL;
+#ifndef ZEND_WIN32
+       close(lock_file);
+#endif
+       locked = 0;
+}
+
+#define SHARED_ALLOC_FAILED() {                \
+       TSRMLS_FETCH();                                 \
+                                                                       \
+       zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %ld bytes (%ld bytes free)", (long)size, (long)ZSMMG(shared_free)); \
+       ZSMMG(memory_exhausted) = 1;                    \
+       zend_accel_schedule_restart(TSRMLS_C);  \
+}
+
+void *zend_shared_alloc(size_t size)
+{
+       int i;
+       unsigned int block_size = size+sizeof(zend_shared_memory_block_header);
+
+#if 1
+       if (!locked) {
+               zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
+       }
+#endif
+       if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
+               SHARED_ALLOC_FAILED();
+               return NULL;
+       }
+       for (i=0; i<ZSMMG(shared_segments_count); i++) {
+               if (ZSMMG(shared_segments)[i]->size-ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
+                       zend_shared_memory_block_header *p = (zend_shared_memory_block_header *) (((char *) ZSMMG(shared_segments)[i]->p)+ZSMMG(shared_segments)[i]->pos);
+                       int remainder = block_size % PLATFORM_ALIGNMENT;
+                       void *retval;
+
+                       if (remainder!=0) {
+                               size += PLATFORM_ALIGNMENT-remainder;
+                               block_size += PLATFORM_ALIGNMENT-remainder;
+                       }
+                       ZSMMG(shared_segments)[i]->pos += block_size;
+                       ZSMMG(shared_free) -= block_size;
+                       p->size = size;
+                       retval = ((char *) p)+sizeof(zend_shared_memory_block_header);
+                       memset(retval, 0, size);
+                       return retval;
+               }
+       }
+       SHARED_ALLOC_FAILED();
+       return NULL;
+}
+
+int zend_shared_memdup_size(void *source, size_t size)
+{
+       void **old_p;
+
+       if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) {
+               /* we already duplicated this pointer */
+               return 0;
+       }
+       zend_shared_alloc_register_xlat_entry(source, source);
+       return ZEND_ALIGNED_SIZE(size);
+}
+
+void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC)
+{
+       void **old_p, *retval;
+
+       if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) {
+               /* we already duplicated this pointer */
+               return *old_p;
+       }
+       retval = ZCG(mem);;
+       ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
+       memcpy(retval, source, size);
+       if (free_source) {
+               interned_efree((char*)source);
+       }
+       zend_shared_alloc_register_xlat_entry(source, retval);
+       return retval;
+}
+
+void zend_shared_alloc_safe_unlock(TSRMLS_D)
+{
+       if (locked) {
+               zend_shared_alloc_unlock(TSRMLS_C);
+       }
+}
+
+#ifndef ZEND_WIN32
+/* name l_type l_whence l_start l_len */
+static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1);
+static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1);
+#endif
+
+void zend_shared_alloc_lock(TSRMLS_D)
+{
+#ifndef ZEND_WIN32
+#if 0
+       /* this will happen once per process, and will un-globalize mem_write_lock */
+       if (mem_write_lock.l_pid == -1) {
+               mem_write_lock.l_pid = getpid();
+       }
+#endif
+
+       while (1) {
+               if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
+               }
+               break;
+       }
+#else
+       zend_shared_alloc_lock_win32();
+#endif
+
+       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, 100, NULL, NULL, 1);
+}
+
+void zend_shared_alloc_unlock(TSRMLS_D)
+{
+       /* Destroy translation table */
+       zend_hash_destroy(&xlat_table);
+
+       locked=0;
+
+#ifndef ZEND_WIN32
+       if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
+               zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
+       }
+#else
+       zend_shared_alloc_unlock_win32();
+#endif
+}
+
+void zend_shared_alloc_clear_xlat_table(void)
+{
+       zend_hash_clean(&xlat_table);
+}
+
+void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
+{
+       zend_hash_index_update(&xlat_table, (ulong) old, (void*)&new, sizeof(void *), NULL);
+}
+
+void *zend_shared_alloc_get_xlat_entry(const void *old)
+{
+       void **retval;
+
+       if (zend_hash_index_find(&xlat_table, (ulong) old, (void **) &retval)==FAILURE) {
+               return NULL;
+       }
+       return *retval;
+}
+
+size_t zend_shared_alloc_get_free_memory(void)
+{
+       return ZSMMG(shared_free);
+}
+
+size_t zend_shared_alloc_get_largest_free_block(void)
+{
+       int i;
+       size_t largest_block_size=0;
+
+       for (i=0; i<ZSMMG(shared_segments_count); i++) {
+               size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
+
+               if (block_size>largest_block_size) {
+                       largest_block_size = block_size;
+               }
+       }
+       return largest_block_size;
+}
+
+void zend_shared_alloc_save_state(void)
+{
+       int i;
+
+       for (i=0; i<ZSMMG(shared_segments_count); i++) {
+               ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
+       }
+       ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
+}
+
+void zend_shared_alloc_restore_state(void)
+{
+       int i;
+
+       for (i=0; i<ZSMMG(shared_segments_count); i++) {
+               ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
+       }
+       ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
+       ZSMMG(memory_exhausted) = 0;
+       ZSMMG(wasted_shared_memory) = 0;
+}
+
+const char *zend_accel_get_shared_model()
+{
+       return g_shared_model;
+}
+
+void zend_accel_shared_protect(int mode TSRMLS_DC)
+{
+#ifdef HAVE_MPROTECT
+       int i;
+
+       if (mode) {
+               mode = PROT_READ;
+       } else {
+               mode = PROT_READ|PROT_WRITE;
+       }
+
+       for(i=0; i < ZSMMG(shared_segments_count); i++) {
+               mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
+       }
+#endif
+}
diff --git a/zend_shared_alloc.h b/zend_shared_alloc.h
new file mode 100644 (file)
index 0000000..aa61514
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend Optimizer+                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Stanislav Malyshev <stas@zend.com>                          |
+   |          Dmitry Stogov <dmitry@zend.com>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_SHARED_ALLOC_H
+#define ZEND_SHARED_ALLOC_H
+
+#include "zend.h"
+
+#if defined(__APPLE__) && defined(__MACH__) /* darwin */
+# define USE_SHM_OPEN   1
+# define USE_MMAP       1
+#elif defined(__linux__) || defined(_AIX)
+# define USE_SHM        1
+# define USE_MMAP       1
+#elif defined(__FreeBSD__)
+# define USE_SHM_OPEN   1
+# define USE_MMAP       1
+# define USE_SHM        1
+#elif defined(__sparc) || defined(__sun)
+# define USE_SHM_OPEN   1
+# define USE_SHM        1
+# if defined(__i386)
+#  define USE_MMAP      1
+# endif
+#endif
+
+#define ALLOC_FAILURE           0
+#define ALLOC_SUCCESS           1
+#define FAILED_REATTACHED       2
+#define SUCCESSFULLY_REATTACHED 4
+#define ALLOC_FAIL_MAPPING      8
+
+typedef struct _zend_shared_segment {
+    size_t  size;
+    size_t  pos;  /* position for simple stack allocator */
+    void   *p;
+} zend_shared_segment;
+
+typedef int (*create_segments_t)(size_t requested_size, zend_shared_segment ***shared_segments, int *shared_segment_count, char **error_in);
+typedef int (*detach_segment_t)(zend_shared_segment *shared_segment);
+
+typedef struct {
+       create_segments_t create_segments;
+       detach_segment_t detach_segment;
+       size_t (*segment_type_size)(void);
+} zend_shared_memory_handlers;
+
+typedef struct _handler_entry {
+       const char                  *name;
+       zend_shared_memory_handlers *handler;
+} zend_shared_memory_handler_entry;
+
+typedef struct _zend_shared_memory_block_header {
+       int size;
+} zend_shared_memory_block_header;
+
+typedef struct _zend_shared_memory_state {
+       int *positions;   /* current positions for each segment */
+       int  shared_free; /* amount of free shared memory */
+} zend_shared_memory_state;
+
+typedef struct _zend_smm_shared_globals {
+    /* Shared Memory Manager */
+    zend_shared_segment      **shared_segments;
+    /* Number of allocated shared segments */
+    int                        shared_segments_count;
+    /* Amount of free shared memory */
+    size_t                     shared_free;
+    /* Amount of shared memory allocated by garbage */
+    int                        wasted_shared_memory;
+    /* No more shared memory flag */
+    zend_bool                  memory_exhausted;
+    /* Saved Shared Allocator State */
+    zend_shared_memory_state   shared_memory_state;
+       /* Pointer to the application's shared data structures */
+       void                      *app_shared_globals;
+} zend_smm_shared_globals;
+
+extern zend_smm_shared_globals *smm_shared_globals;
+
+#define ZSMMG(element)         (smm_shared_globals->element)
+
+#define SHARED_ALLOC_REATTACHED                (SUCCESS+1)
+
+int zend_shared_alloc_startup(int requested_size);
+void zend_shared_alloc_shutdown(void);
+
+/* allocate shared memory block */
+void *zend_shared_alloc(size_t size);
+
+/* copy into shared memory */
+void *_zend_shared_memdup(void *p, size_t size, zend_bool free_source TSRMLS_DC);
+int  zend_shared_memdup_size(void *p, size_t size);
+
+typedef union _align_test {
+       void   *ptr;
+       double  dbl;
+       long    lng;
+} align_test;
+
+#if ZEND_GCC_VERSION >= 2000
+# define PLATFORM_ALIGNMENT (__alignof__ (align_test))
+#else
+# define PLATFORM_ALIGNMENT (sizeof(align_test))
+#endif
+
+#define ZEND_ALIGNED_SIZE(size) \
+       ((size + PLATFORM_ALIGNMENT - 1) & ~(PLATFORM_ALIGNMENT - 1))
+
+/* exclusive locking */
+void zend_shared_alloc_lock(TSRMLS_D);
+void zend_shared_alloc_unlock(TSRMLS_D); /* returns the allocated size during lock..unlock */
+void zend_shared_alloc_safe_unlock(TSRMLS_D);
+
+/* old/new mapping functions */
+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);
+
+size_t zend_shared_alloc_get_free_memory(void);
+void zend_shared_alloc_save_state(void);
+void zend_shared_alloc_restore_state(void);
+size_t zend_shared_alloc_get_largest_free_block(void);
+const char *zend_accel_get_shared_model();
+
+/* memory write protection */
+void zend_accel_shared_protect(int mode TSRMLS_DC);
+
+#ifdef USE_MMAP
+extern zend_shared_memory_handlers zend_alloc_mmap_handlers;
+#endif
+
+#ifdef USE_SHM
+extern zend_shared_memory_handlers zend_alloc_shm_handlers;
+#endif
+
+#ifdef USE_SHM_OPEN
+extern zend_shared_memory_handlers zend_alloc_posix_handlers;
+#endif
+
+#ifdef ZEND_WIN32
+extern zend_shared_memory_handlers zend_alloc_win32_handlers;
+void zend_shared_alloc_create_lock();
+void zend_shared_alloc_lock_win32();
+void zend_shared_alloc_unlock_win32();
+#endif
+
+#endif /* ZEND_SHARED_ALLOC_H */