source: trunk/xcache.c @ 939

Last change on this file since 939 was 939, checked in by moo, 22 months ago

no need to add to open_files

  • Property svn:eol-style set to native
File size: 117.2 KB
Line 
1
2#if 0
3#define XCACHE_DEBUG
4#endif
5
6#if 0
7#define SHOW_DPRINT
8#endif
9
10/* {{{ macros */
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14
15#include <signal.h>
16
17#include "php.h"
18#include "ext/standard/info.h"
19#include "ext/standard/md5.h"
20#include "ext/standard/php_math.h"
21#include "ext/standard/php_string.h"
22#include "zend_extensions.h"
23#include "SAPI.h"
24
25#include "xcache.h"
26#ifdef ZEND_ENGINE_2_1
27#include "ext/date/php_date.h"
28#endif
29#include "optimizer.h"
30#include "coverager.h"
31#include "disassembler.h"
32#include "align.h"
33#include "stack.h"
34#include "xcache_globals.h"
35#include "processor.h"
36#include "const_string.h"
37#include "opcode_spec.h"
38#include "utils.h"
39
40#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > (pentry)->ctime + (time_t) (pentry)->ttl)
41#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
42#define LOCK(x) xc_lock((x)->lck)
43#define UNLOCK(x) xc_unlock((x)->lck)
44
45#define ENTER_LOCK_EX(x) \
46    xc_lock((x)->lck); \
47    zend_try { \
48        do
49#define LEAVE_LOCK_EX(x) \
50        while (0); \
51    } zend_catch { \
52        catched = 1; \
53    } zend_end_try(); \
54    xc_unlock((x)->lck)
55
56#define ENTER_LOCK(x) do { \
57    int catched = 0; \
58    ENTER_LOCK_EX(x)
59#define LEAVE_LOCK(x) \
60    LEAVE_LOCK_EX(x); \
61    if (catched) { \
62        zend_bailout(); \
63    } \
64} while(0)
65
66/* }}} */
67
68/* {{{ globals */
69static char *xc_shm_scheme = NULL;
70static char *xc_mmap_path = NULL;
71static char *xc_coredump_dir = NULL;
72
73static xc_hash_t xc_php_hcache = { 0 };
74static xc_hash_t xc_php_hentry = { 0 };
75static xc_hash_t xc_var_hcache = { 0 };
76static xc_hash_t xc_var_hentry = { 0 };
77
78static zend_ulong xc_php_ttl    = 0;
79static zend_ulong xc_var_maxttl = 0;
80
81enum { xc_deletes_gc_interval = 120 };
82static zend_ulong xc_php_gc_interval = 0;
83static zend_ulong xc_var_gc_interval = 0;
84
85/* total size */
86static zend_ulong xc_php_size  = 0;
87static zend_ulong xc_var_size  = 0;
88
89static xc_cache_t **xc_php_caches = NULL;
90static xc_cache_t **xc_var_caches = NULL;
91
92static zend_bool xc_initized = 0;
93static time_t xc_init_time = 0;
94static long unsigned xc_init_instance_id = 0;
95#ifdef ZTS
96static long unsigned xc_init_instance_subid = 0;
97#endif
98static zend_compile_file_t *origin_compile_file = NULL;
99static zend_compile_file_t *old_compile_file = NULL;
100static zend_llist_element  *xc_llist_zend_extension = NULL;
101
102static zend_bool xc_test = 0;
103static zend_bool xc_readonly_protection = 0;
104
105zend_bool xc_have_op_array_ctor = 0;
106
107static zend_bool xc_module_gotup = 0;
108static zend_bool xc_zend_extension_gotup = 0;
109static zend_bool xc_zend_extension_faked = 0;
110#if !COMPILE_DL_XCACHE
111#   define zend_extension_entry xcache_zend_extension_entry
112#endif
113ZEND_DLEXPORT zend_extension zend_extension_entry;
114ZEND_DECLARE_MODULE_GLOBALS(xcache);
115
116typedef enum { XC_TYPE_PHP, XC_TYPE_VAR } xc_entry_type_t;
117/* }}} */
118
119/* any function in *_unlocked is only safe be called within locked (single thread access) area */
120
121static void xc_php_add_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
122{
123    xc_entry_data_php_t **head = &(cache->phps[php->hvalue]);
124    php->next = *head;
125    *head = php;
126    cache->phps_count ++;
127}
128/* }}} */
129static xc_entry_data_php_t *xc_php_store_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
130{
131    xc_entry_data_php_t *stored_php;
132
133    php->hits     = 0;
134    php->refcount = 0;
135    stored_php = xc_processor_store_xc_entry_data_php_t(cache, php TSRMLS_CC);
136    if (stored_php) {
137        xc_php_add_unlocked(cache, stored_php);
138        return stored_php;
139    }
140    else {
141        cache->ooms ++;
142        return NULL;
143    }
144}
145/* }}} */
146static xc_entry_data_php_t *xc_php_find_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
147{
148    xc_entry_data_php_t *p;
149    for (p = cache->phps[php->hvalue]; p; p = (xc_entry_data_php_t *) p->next) {
150        if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
151            p->hits ++;
152            return p;
153        }
154    }
155    return NULL;
156}
157/* }}} */
158static void xc_php_free_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
159{
160    cache->mem->handlers->free(cache->mem, (xc_entry_data_php_t *)php);
161}
162/* }}} */
163static void xc_php_addref_unlocked(xc_entry_data_php_t *php) /* {{{ */
164{
165    php->refcount ++;
166}
167/* }}} */
168static void xc_php_release_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
169{
170    if (-- php->refcount == 0) {
171        xc_entry_data_php_t **pp = &(cache->phps[php->hvalue]);
172        xc_entry_data_php_t *p;
173        for (p = *pp; p; pp = &(p->next), p = p->next) {
174            if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
175                /* unlink */
176                *pp = p->next;
177                xc_php_free_unlocked(cache, php);
178                return;
179            }
180        }
181        assert(0);
182    }
183}
184/* }}} */
185
186static inline int xc_entry_equal_unlocked(xc_entry_type_t type, const xc_entry_t *entry1, const xc_entry_t *entry2) /* {{{ */
187{
188    /* this function isn't required but can be in unlocked */
189    switch (type) {
190        case XC_TYPE_PHP:
191#ifdef HAVE_INODE
192            {
193                const xc_entry_php_t *php_entry1 = (const xc_entry_php_t *) entry1;
194                const xc_entry_php_t *php_entry2 = (const xc_entry_php_t *) entry2;
195                if (php_entry1->file_inode) {
196                    return php_entry1->file_inode == php_entry2->file_inode
197                        && php_entry1->file_device == php_entry2->file_device;
198                }
199            }
200#endif
201            assert(IS_ABSOLUTE_PATH(entry1->name.str.val, entry1->name.str.len));
202            assert(IS_ABSOLUTE_PATH(entry2->name.str.val, entry2->name.str.len));
203
204            if (entry1->name.str.len != entry2->name.str.len) {
205                return 0;
206            }
207            return memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
208
209        case XC_TYPE_VAR:
210#ifdef IS_UNICODE
211            if (entry1->name_type != entry2->name_type) {
212                return 0;
213            }
214            else if (entry1->name_type == IS_UNICODE) {
215                if (entry1->name.ustr.len != entry2->name.ustr.len) {
216                    return 0;
217                }
218                return memcmp(entry1->name.ustr.val, entry2->name.ustr.val, (entry1->name.ustr.len + 1) * sizeof(UChar)) == 0;
219            }
220            else
221#endif
222            {
223                if (entry1->name.str.len != entry2->name.str.len) {
224                    return 0;
225                }
226                return memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
227            }
228            break;
229
230        default:
231            assert(0);
232    }
233    return 0;
234}
235/* }}} */
236static inline int xc_entry_has_prefix_unlocked(xc_entry_type_t type, xc_entry_t *entry, zval *prefix) /* {{{ */
237{
238    /* this function isn't required but can be in unlocked */
239
240#ifdef IS_UNICODE
241    if (entry->name_type != prefix->type) {
242        return 0;
243    }
244
245    if (entry->name_type == IS_UNICODE) {
246        if (entry->name.ustr.len < Z_USTRLEN_P(prefix)) {
247            return 0;
248        }
249        return memcmp(entry->name.ustr.val, Z_USTRVAL_P(prefix), Z_USTRLEN_P(prefix) * sizeof(UChar)) == 0;
250    }
251#endif
252    if (prefix->type != IS_STRING) {
253        return 0;
254    }
255
256    if (entry->name.str.len < Z_STRLEN_P(prefix)) {
257        return 0;
258    }
259
260    return memcmp(entry->name.str.val, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)) == 0;
261}
262/* }}} */
263static void xc_entry_add_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry) /* {{{ */
264{
265    xc_entry_t **head = &(cache->entries[entryslotid]);
266    entry->next = *head;
267    *head = entry;
268    cache->entries_count ++;
269}
270/* }}} */
271static xc_entry_t *xc_entry_store_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
272{
273    xc_entry_t *stored_entry;
274
275    entry->hits  = 0;
276    entry->ctime = XG(request_time);
277    entry->atime = XG(request_time);
278    stored_entry = type == XC_TYPE_PHP
279        ? (xc_entry_t *) xc_processor_store_xc_entry_php_t(cache, (xc_entry_php_t *) entry TSRMLS_CC)
280        : (xc_entry_t *) xc_processor_store_xc_entry_var_t(cache, (xc_entry_var_t *) entry TSRMLS_CC);
281    if (stored_entry) {
282        xc_entry_add_unlocked(cache, entryslotid, stored_entry);
283        ++cache->updates;
284        return stored_entry;
285    }
286    else {
287        cache->ooms ++;
288        return NULL;
289    }
290}
291/* }}} */
292static xc_entry_php_t *xc_entry_php_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
293{
294    return (xc_entry_php_t *) xc_entry_store_unlocked(XC_TYPE_PHP, cache, entryslotid, (xc_entry_t *) entry_php TSRMLS_CC);
295}
296/* }}} */
297static xc_entry_var_t *xc_entry_var_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_var_t *entry_var TSRMLS_DC) /* {{{ */
298{
299    return (xc_entry_var_t *) xc_entry_store_unlocked(XC_TYPE_VAR, cache, entryslotid, (xc_entry_t *) entry_var TSRMLS_CC);
300}
301/* }}} */
302static void xc_entry_free_real_unlocked(xc_entry_type_t type, xc_cache_t *cache, volatile xc_entry_t *entry) /* {{{ */
303{
304    if (type == XC_TYPE_PHP) {
305        xc_php_release_unlocked(cache, ((xc_entry_php_t *) entry)->php);
306    }
307    cache->mem->handlers->free(cache->mem, (xc_entry_t *)entry);
308}
309/* }}} */
310static void xc_entry_free_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_entry_t *entry TSRMLS_DC) /* {{{ */
311{
312    cache->entries_count --;
313    if ((type == XC_TYPE_PHP ? ((xc_entry_php_t *) entry)->refcount : 0) == 0) {
314        xc_entry_free_real_unlocked(type, cache, entry);
315    }
316    else {
317        entry->next = cache->deletes;
318        cache->deletes = entry;
319        entry->dtime = XG(request_time);
320        cache->deletes_count ++;
321    }
322    return;
323}
324/* }}} */
325static void xc_entry_remove_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
326{
327    xc_entry_t **pp = &(cache->entries[entryslotid]);
328    xc_entry_t *p;
329    for (p = *pp; p; pp = &(p->next), p = p->next) {
330        if (xc_entry_equal_unlocked(type, entry, p)) {
331            /* unlink */
332            *pp = p->next;
333            xc_entry_free_unlocked(type, cache, entry TSRMLS_CC);
334            return;
335        }
336    }
337    assert(0);
338}
339/* }}} */
340static xc_entry_t *xc_entry_find_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
341{
342    xc_entry_t *p;
343    for (p = cache->entries[entryslotid]; p; p = p->next) {
344        if (xc_entry_equal_unlocked(type, entry, p)) {
345            zend_bool fresh;
346            switch (type) {
347            case XC_TYPE_PHP:
348                {
349                    xc_entry_php_t *p_php = (xc_entry_php_t *) p;
350                    xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
351                    fresh = p_php->file_mtime == entry_php->file_mtime && p_php->file_size == entry_php->file_size;
352                }
353                break;
354
355            case XC_TYPE_VAR:
356                {
357                    fresh = !VAR_ENTRY_EXPIRED(p);
358                }
359                break;
360
361            default:
362                assert(0);
363            }
364
365            if (fresh) {
366                p->hits ++;
367                p->atime = XG(request_time);
368                return p;
369            }
370
371            xc_entry_remove_unlocked(type, cache, entryslotid, p TSRMLS_CC);
372            return NULL;
373        }
374    }
375    return NULL;
376}
377/* }}} */
378static void xc_entry_hold_php_unlocked(xc_cache_t *cache, xc_entry_php_t *entry TSRMLS_DC) /* {{{ */
379{
380    TRACE("hold %d:%s", entry->file_inode, entry->entry.name.str.val);
381    entry->refcount ++;
382    xc_stack_push(&XG(php_holds)[cache->cacheid], (void *)entry);
383}
384/* }}} */
385static inline zend_uint advance_wrapped(zend_uint val, zend_uint count) /* {{{ */
386{
387    if (val + 1 >= count) {
388        return 0;
389    }
390    return val + 1;
391}
392/* }}} */
393static void xc_counters_inc(time_t *curtime, zend_uint *curslot, time_t period, zend_ulong *counters, zend_uint count TSRMLS_DC) /* {{{ */
394{
395    time_t n = XG(request_time) / period;
396    if (*curtime != n) {
397        zend_uint target_slot = n % count;
398        if (n - *curtime > period) {
399            memset(counters, 0, sizeof(counters[0]) * count);
400        }
401        else {
402            zend_uint slot;
403            for (slot = advance_wrapped(*curslot, count);
404                    slot != target_slot;
405                    slot = advance_wrapped(slot, count)) {
406                counters[slot] = 0;
407            }
408            counters[target_slot] = 0;
409        }
410        *curtime = n;
411        *curslot = target_slot;
412    }
413    counters[*curslot] ++;
414}
415/* }}} */
416static void xc_cache_hit_unlocked(xc_cache_t *cache TSRMLS_DC) /* {{{ */
417{
418    cache->hits ++;
419
420    xc_counters_inc(&cache->hits_by_hour_cur_time
421            , &cache->hits_by_hour_cur_slot, 60 * 60
422            , cache->hits_by_hour
423            , sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0])
424            TSRMLS_CC);
425
426    xc_counters_inc(&cache->hits_by_second_cur_time
427            , &cache->hits_by_second_cur_slot
428            , 1
429            , cache->hits_by_second
430            , sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0])
431            TSRMLS_CC);
432}
433/* }}} */
434
435/* helper function that loop through each entry */
436#define XC_ENTRY_APPLY_FUNC(name) zend_bool name(xc_entry_t *entry TSRMLS_DC)
437typedef XC_ENTRY_APPLY_FUNC((*cache_apply_unlocked_func_t));
438static void xc_entry_apply_unlocked(xc_entry_type_t type, xc_cache_t *cache, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
439{
440    xc_entry_t *p, **pp;
441    int i, c;
442
443    for (i = 0, c = cache->hentry->size; i < c; i ++) {
444        pp = &(cache->entries[i]);
445        for (p = *pp; p; p = *pp) {
446            if (apply_func(p TSRMLS_CC)) {
447                /* unlink */
448                *pp = p->next;
449                xc_entry_free_unlocked(type, cache, p TSRMLS_CC);
450            }
451            else {
452                pp = &(p->next);
453            }
454        }
455    }
456}
457/* }}} */
458
459#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
460/* call graph:
461 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_php_entry_unlocked
462 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_var_entry_unlocked
463 */
464static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_unlocked) /* {{{ */
465{
466    TRACE("ttl %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
467    if (XG(request_time) > entry->atime + (time_t) xc_php_ttl) {
468        return 1;
469    }
470    return 0;
471}
472/* }}} */
473static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_unlocked) /* {{{ */
474{
475    if (VAR_ENTRY_EXPIRED(entry)) {
476        return 1;
477    }
478    return 0;
479}
480/* }}} */
481static void xc_gc_expires_one(xc_entry_type_t type, xc_cache_t *cache, zend_ulong gc_interval, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
482{
483    TRACE("interval %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) cache->last_gc_expires, gc_interval);
484    if (XG(request_time) >= cache->last_gc_expires + (time_t) gc_interval) {
485        ENTER_LOCK(cache) {
486            if (XG(request_time) >= cache->last_gc_expires + (time_t) gc_interval) {
487                cache->last_gc_expires = XG(request_time);
488                xc_entry_apply_unlocked(type, cache, apply_func TSRMLS_CC);
489            }
490        } LEAVE_LOCK(cache);
491    }
492}
493/* }}} */
494static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
495{
496    int i, c;
497
498    if (!xc_php_ttl || !xc_php_gc_interval || !xc_php_caches) {
499        return;
500    }
501
502    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
503        xc_gc_expires_one(XC_TYPE_PHP, xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_unlocked TSRMLS_CC);
504    }
505}
506/* }}} */
507static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
508{
509    int i, c;
510
511    if (!xc_var_gc_interval || !xc_var_caches) {
512        return;
513    }
514
515    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
516        xc_gc_expires_one(XC_TYPE_VAR, xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_unlocked TSRMLS_CC);
517    }
518}
519/* }}} */
520
521static XC_CACHE_APPLY_FUNC(xc_gc_delete_unlocked) /* {{{ */
522{
523    xc_entry_t *p, **pp;
524
525    pp = &cache->deletes;
526    for (p = *pp; p; p = *pp) {
527        xc_entry_php_t *entry = (xc_entry_php_t *) p;
528        if (XG(request_time) - p->dtime > 3600) {
529            entry->refcount = 0;
530            /* issue warning here */
531        }
532        if (entry->refcount == 0) {
533            /* unlink */
534            *pp = p->next;
535            cache->deletes_count --;
536            xc_entry_free_real_unlocked(XC_TYPE_PHP, cache, p);
537        }
538        else {
539            pp = &(p->next);
540        }
541    }
542}
543/* }}} */
544static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
545{
546    if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
547        ENTER_LOCK(cache) {
548            if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
549                cache->last_gc_deletes = XG(request_time);
550                xc_gc_delete_unlocked(cache TSRMLS_CC);
551            }
552        } LEAVE_LOCK(cache);
553    }
554}
555/* }}} */
556static void xc_gc_deletes(TSRMLS_D) /* {{{ */
557{
558    int i, c;
559
560    if (xc_php_caches) {
561        for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
562            xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
563        }
564    }
565
566    if (xc_var_caches) {
567        for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
568            xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
569        }
570    }
571}
572/* }}} */
573
574/* helper functions for user functions */
575static void xc_fillinfo_unlocked(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
576{
577    zval *blocks, *hits;
578    int i;
579    const xc_block_t *b;
580#ifndef NDEBUG
581    xc_memsize_t avail = 0;
582#endif
583    xc_mem_t *mem = cache->mem;
584    const xc_mem_handlers_t *handlers = mem->handlers;
585    zend_ulong interval;
586    if (cachetype == XC_TYPE_PHP) {
587        interval = xc_php_ttl ? xc_php_gc_interval : 0;
588    }
589    else {
590        interval = xc_var_gc_interval;
591    }
592
593    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
594    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
595    add_assoc_long_ex(return_value, ZEND_STRS("updates"),   cache->updates);
596    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->updates); /* deprecated */
597    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
598    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
599    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
600    add_assoc_long_ex(return_value, ZEND_STRS("errors"),    cache->errors);
601
602    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cache->entries_count);
603    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cache->deletes_count);
604    if (interval) {
605        time_t gc = (cache->last_gc_expires + interval) - XG(request_time);
606        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    gc > 0 ? gc : 0);
607    }
608    else {
609        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
610    }
611    MAKE_STD_ZVAL(hits);
612    array_init(hits);
613    for (i = 0; i < sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0]); i ++) {
614        add_next_index_long(hits, (long) cache->hits_by_hour[i]);
615    }
616    add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_hour"), hits);
617
618    MAKE_STD_ZVAL(hits);
619    array_init(hits);
620    for (i = 0; i < sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0]); i ++) {
621        add_next_index_long(hits, (long) cache->hits_by_second[i]);
622    }
623    add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_second"), hits);
624
625    MAKE_STD_ZVAL(blocks);
626    array_init(blocks);
627
628    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
629    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
630    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
631
632    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
633        zval *bi;
634
635        MAKE_STD_ZVAL(bi);
636        array_init(bi);
637
638        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
639        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
640        add_next_index_zval(blocks, bi);
641#ifndef NDEBUG
642        avail += handlers->block_size(b);
643#endif
644    }
645    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
646#ifndef NDEBUG
647    assert(avail == handlers->avail(mem));
648#endif
649}
650/* }}} */
651static void xc_fillentry_unlocked(xc_entry_type_t type, const xc_entry_t *entry, xc_hash_value_t entryslotid, int del, zval *list TSRMLS_DC) /* {{{ */
652{
653    zval* ei;
654    const xc_entry_data_php_t *php;
655
656    ALLOC_INIT_ZVAL(ei);
657    array_init(ei);
658
659    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
660    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
661    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
662    add_assoc_long_ex(ei, ZEND_STRS("hvalue"),   entryslotid);
663    if (del) {
664        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
665    }
666#ifdef IS_UNICODE
667    do {
668        zval *zv;
669        ALLOC_INIT_ZVAL(zv);
670        switch (entry->name_type) {
671            case IS_UNICODE:
672                ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
673                break;
674            case IS_STRING:
675                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
676                break;
677            default:
678                assert(0);
679        }
680        zv->type = entry->name_type;
681        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
682    } while (0);
683#else
684    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
685#endif
686    switch (type) {
687        case XC_TYPE_PHP: {
688            xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
689            php = entry_php->php;
690            add_assoc_long_ex(ei, ZEND_STRS("size"),          entry->size + php->size);
691            add_assoc_long_ex(ei, ZEND_STRS("refcount"),      entry_php->refcount);
692            add_assoc_long_ex(ei, ZEND_STRS("phprefcount"),   php->refcount);
693            add_assoc_long_ex(ei, ZEND_STRS("file_mtime"),    entry_php->file_mtime);
694            add_assoc_long_ex(ei, ZEND_STRS("file_size"),     entry_php->file_size);
695#ifdef HAVE_INODE
696            add_assoc_long_ex(ei, ZEND_STRS("file_device"),   entry_php->file_device);
697            add_assoc_long_ex(ei, ZEND_STRS("file_inode"),    entry_php->file_inode);
698#endif
699
700#ifdef HAVE_XCACHE_CONSTANT
701            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
702#endif
703            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
704            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
705#ifdef ZEND_ENGINE_2_1
706            add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
707#endif
708            break;
709        }
710
711        case XC_TYPE_VAR:
712            add_assoc_long_ex(ei, ZEND_STRS("size"),          entry->size);
713            break;
714
715        default:
716            assert(0);
717    }
718
719    add_next_index_zval(list, ei);
720}
721/* }}} */
722static void xc_filllist_unlocked(xc_entry_type_t type, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
723{
724    zval* list;
725    int i, c;
726    xc_entry_t *e;
727
728    ALLOC_INIT_ZVAL(list);
729    array_init(list);
730
731    for (i = 0, c = cache->hentry->size; i < c; i ++) {
732        for (e = cache->entries[i]; e; e = e->next) {
733            xc_fillentry_unlocked(type, e, i, 0, list TSRMLS_CC);
734        }
735    }
736    add_assoc_zval(return_value, "cache_list", list);
737
738    ALLOC_INIT_ZVAL(list);
739    array_init(list);
740    for (e = cache->deletes; e; e = e->next) {
741        xc_fillentry_unlocked(XC_TYPE_PHP, e, 0, 1, list TSRMLS_CC);
742    }
743    add_assoc_zval(return_value, "deleted_list", list);
744}
745/* }}} */
746
747static zend_op_array *xc_entry_install(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
748{
749    zend_uint i;
750    xc_entry_data_php_t *p = entry_php->php;
751    zend_op_array *old_active_op_array = CG(active_op_array);
752#ifndef ZEND_ENGINE_2
753    ALLOCA_FLAG(use_heap)
754    /* new ptr which is stored inside CG(class_table) */
755    xc_cest_t **new_cest_ptrs = (xc_cest_t **)my_do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt, use_heap);
756#endif
757
758    CG(active_op_array) = p->op_array;
759
760#ifdef HAVE_XCACHE_CONSTANT
761    /* install constant */
762    for (i = 0; i < p->constinfo_cnt; i ++) {
763        xc_constinfo_t *ci = &p->constinfos[i];
764        xc_install_constant(entry_php->entry.name.str.val, &ci->constant,
765                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
766    }
767#endif
768
769    /* install function */
770    for (i = 0; i < p->funcinfo_cnt; i ++) {
771        xc_funcinfo_t  *fi = &p->funcinfos[i];
772        xc_install_function(entry_php->entry.name.str.val, &fi->func,
773                UNISW(0, fi->type), fi->key, fi->key_size, fi->h TSRMLS_CC);
774    }
775
776    /* install class */
777    for (i = 0; i < p->classinfo_cnt; i ++) {
778        xc_classinfo_t *ci = &p->classinfos[i];
779#ifndef ZEND_ENGINE_2
780        zend_class_entry *ce = CestToCePtr(ci->cest);
781        /* fix pointer to the be which inside class_table */
782        if (ce->parent) {
783            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
784            assert(class_idx < i);
785            ci->cest.parent = new_cest_ptrs[class_idx];
786        }
787        new_cest_ptrs[i] =
788#endif
789#ifdef ZEND_COMPILE_DELAYED_BINDING
790        xc_install_class(entry_php->entry.name.str.val, &ci->cest, -1,
791                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
792#else
793        xc_install_class(entry_php->entry.name.str.val, &ci->cest, ci->oplineno,
794                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
795#endif
796    }
797
798#ifdef ZEND_ENGINE_2_1
799    /* trigger auto_globals jit */
800    for (i = 0; i < p->autoglobal_cnt; i ++) {
801        xc_autoglobal_t *aginfo = &p->autoglobals[i];
802        zend_u_is_auto_global(aginfo->type, aginfo->key, aginfo->key_len TSRMLS_CC);
803    }
804#endif
805#ifdef XCACHE_ERROR_CACHING
806    /* restore trigger errors */
807    for (i = 0; i < p->compilererror_cnt; i ++) {
808        xc_compilererror_t *error = &p->compilererrors[i];
809        CG(zend_lineno) = error->lineno;
810        zend_error(error->type, "%s", error->error);
811    }
812    CG(zend_lineno) = 0;
813#endif
814
815    i = 1;
816    zend_hash_add(&EG(included_files), entry_php->entry.name.str.val, entry_php->entry.name.str.len+1, (void *)&i, sizeof(int), NULL);
817
818#ifndef ZEND_ENGINE_2
819    my_free_alloca(new_cest_ptrs, use_heap);
820#endif
821    CG(active_op_array) = old_active_op_array;
822    return p->op_array;
823}
824/* }}} */
825
826static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
827{
828    int i;
829    xc_stack_t *s;
830    xc_cache_t *cache;
831    xc_entry_php_t *entry_php;
832
833    for (i = 0; i < cachecount; i ++) {
834        s = &holds[i];
835        TRACE("holded %d items", xc_stack_count(s));
836        if (xc_stack_count(s)) {
837            cache = caches[i];
838            ENTER_LOCK(cache) {
839                while (xc_stack_count(s)) {
840                    entry_php = (xc_entry_php_t *) xc_stack_pop(s);
841                    TRACE("unhold %d:%s", entry_php->file_inode, entry_php->entry.name.str.val);
842                    --entry_php->refcount;
843                    assert(entry_php->refcount >= 0);
844                }
845            } LEAVE_LOCK(cache);
846        }
847    }
848}
849/* }}} */
850static void xc_entry_unholds(TSRMLS_D) /* {{{ */
851{
852    if (xc_php_caches) {
853        xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
854    }
855
856    if (xc_var_caches) {
857        xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
858    }
859}
860/* }}} */
861
862#define HASH(i) (i)
863#define HASH_ZSTR_L(t, s, l) HASH(zend_u_inline_hash_func((t), (s), ((l) + 1) * sizeof(UChar)))
864#define HASH_STR_S(s, l) HASH(zend_inline_hash_func((char *) (s), (l)))
865#define HASH_STR_L(s, l) HASH_STR_S((s), (l) + 1)
866#define HASH_STR(s) HASH_STR_L((s), strlen((s)) + 1)
867#define HASH_NUM(n) HASH(n)
868static inline xc_hash_value_t xc_hash_fold(xc_hash_value_t hvalue, const xc_hash_t *hasher) /* {{{ fold hash bits as needed */
869{
870    xc_hash_value_t folded = 0;
871    while (hvalue) {
872        folded ^= (hvalue & hasher->mask);
873        hvalue >>= hasher->bits;
874    }
875    return folded;
876}
877/* }}} */
878static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *entry TSRMLS_DC) /* {{{ */
879{
880    return UNISW(NOTHING, UG(unicode) ? HASH_ZSTR_L(entry->name_type, entry->name.uni.val, entry->name.uni.len) :)
881        HASH_STR_L(entry->name.str.val, entry->name.str.len);
882}
883/* }}} */
884#define xc_entry_hash_var xc_entry_hash_name
885static void xc_entry_free_key_php(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
886{
887#define X_FREE(var) do {\
888    if (entry_php->var) { \
889        efree(entry_php->var); \
890    } \
891} while (0)
892    X_FREE(dirpath);
893#ifdef IS_UNICODE
894    X_FREE(ufilepath);
895    X_FREE(udirpath);
896#endif
897
898#undef X_FREE
899}
900/* }}} */
901static char *xc_expand_url(const char *filepath, char *real_path TSRMLS_DC) /* {{{ */
902{
903    if (strstr(filepath, "://") != NULL) {
904        size_t filepath_len = strlen(filepath);
905        size_t copy_len = filepath_len > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : filepath_len;
906        memcpy(real_path, filepath, filepath_len);
907        real_path[copy_len] = '\0';
908        return real_path;
909    }
910    return expand_filepath(filepath, real_path TSRMLS_CC);
911}
912/* }}} */
913
914#define XC_RESOLVE_PATH_CHECKER(name) zend_bool name(const char *filepath, size_t filepath_len, void *data TSRMLS_DC)
915typedef XC_RESOLVE_PATH_CHECKER((*xc_resolve_path_checker_func_t));
916static zend_bool xc_resolve_path(const char *filepath, char *path_buffer, xc_resolve_path_checker_func_t checker_func, void *data TSRMLS_DC) /* {{{ */
917{
918    char *paths, *path;
919    char *tokbuf;
920    size_t path_buffer_len;
921    int size;
922    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
923    int ret;
924    ALLOCA_FLAG(use_heap)
925
926#if 0
927    if ((*filepath == '.' &&
928         (IS_SLASH(filepath[1]) ||
929          ((filepath[1] == '.') && IS_SLASH(filepath[2])))) ||
930        IS_ABSOLUTE_PATH(filepath, strlen(filepath)) ||
931        !path ||
932        !*path) {
933
934        if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
935            ret = 1;
936        }
937        else {
938            ret = FAILURE;
939        }
940        goto finish;
941    }
942#endif
943
944    size = strlen(PG(include_path)) + 1;
945    paths = (char *)my_do_alloca(size, use_heap);
946    memcpy(paths, PG(include_path), size);
947
948    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
949        path_buffer_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", path, filepath);
950        if (path_buffer_len < MAXPATHLEN - 1) {
951            if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
952                ret = 1;
953                goto finish;
954            }
955        }
956    }
957
958    /* fall back to current directory */
959    if (zend_is_executing(TSRMLS_C)) {
960        const char *executed_filename = zend_get_executed_filename(TSRMLS_C);
961        if (executed_filename && executed_filename[0] && executed_filename[0] != '[') {
962            size_t filename_len = strlen(filepath);
963            size_t dirname_len;
964
965            for (dirname_len = strlen(executed_filename) - 1; dirname_len > 0; --dirname_len) {
966                if (IS_SLASH(executed_filename[dirname_len])) {
967                    if (dirname_len + filename_len < MAXPATHLEN - 1) {
968                        ++dirname_len; /* include tailing slash */
969                        memcpy(path_buffer, executed_filename, dirname_len);
970                        memcpy(path_buffer + dirname_len, filepath, filename_len);
971                        path_buffer_len = dirname_len + filename_len;
972                        path_buffer[path_buffer_len] = '\0';
973                        if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC) == 0) {
974                            ret = 1;
975                            goto finish;
976                        }
977                    }
978                    break;
979                }
980            }
981        }
982    }
983
984    ret = FAILURE;
985
986finish:
987    my_free_alloca(paths, use_heap);
988
989    return ret;
990}
991/* }}} */
992#ifndef ZEND_ENGINE_2_3
993static XC_RESOLVE_PATH_CHECKER(xc_stat_file) /* {{{ */
994{
995    return VCWD_STAT(filepath, (struct stat *) data) == 0 ? 1 : 0;
996}
997/* }}} */
998static int xc_resolve_path_stat(const char *filepath, char *path_buffer, struct stat *pbuf TSRMLS_DC) /* {{{ */
999{
1000    return xc_resolve_path(filepath, path_buffer, xc_stat_file, (void *) pbuf TSRMLS_CC)
1001        ? SUCCESS
1002        : FAILURE;
1003}
1004/* }}} */
1005#endif
1006typedef struct xc_compiler_t { /* {{{ */
1007    /* XCache cached compile state */
1008    const char *filename;
1009    size_t filename_len;
1010    const char *opened_path;
1011    char opened_path_buffer[MAXPATHLEN];
1012
1013    xc_entry_hash_t entry_hash;
1014    xc_entry_php_t new_entry;
1015    xc_entry_data_php_t new_php;
1016} xc_compiler_t;
1017/* }}} */
1018typedef struct xc_entry_resolve_path_data_t { /* {{{ */
1019    xc_compiler_t *compiler;
1020    xc_entry_php_t **stored_entry;
1021} xc_entry_resolve_path_data_t;
1022/* }}} */
1023static XC_RESOLVE_PATH_CHECKER(xc_entry_resolve_path_func_unlocked) /* {{{ */
1024{
1025    xc_entry_resolve_path_data_t *entry_resolve_path_data = (xc_entry_resolve_path_data_t *) data;
1026    xc_compiler_t *compiler = entry_resolve_path_data->compiler;
1027
1028    compiler->new_entry.entry.name.str.val = xc_expand_url(filepath, compiler->opened_path_buffer TSRMLS_CC);
1029    compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
1030
1031    *entry_resolve_path_data->stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(
1032            XC_TYPE_PHP
1033            , xc_php_caches[compiler->entry_hash.cacheid]
1034            , compiler->entry_hash.entryslotid
1035            , (xc_entry_t *) &compiler->new_entry
1036            TSRMLS_CC);
1037
1038    return *entry_resolve_path_data->stored_entry ? 1 : 0;
1039}
1040/* }}} */
1041static int xc_entry_resolve_path_unlocked(xc_compiler_t *compiler, const char *filepath, xc_entry_php_t **stored_entry TSRMLS_DC) /* {{{ */
1042{
1043    char path_buffer[MAXPATHLEN];
1044    xc_entry_resolve_path_data_t entry_resolve_path_data;
1045    entry_resolve_path_data.compiler = compiler;
1046    entry_resolve_path_data.stored_entry = stored_entry;
1047
1048    return xc_resolve_path(filepath, path_buffer, xc_entry_resolve_path_func_unlocked, (void *) &entry_resolve_path_data TSRMLS_CC)
1049        ? SUCCESS
1050        : FAILURE;
1051}
1052/* }}} */
1053static int xc_entry_php_quick_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1054{
1055    if (strcmp(SG(request_info).path_translated, compiler->filename) == 0) {
1056        /* sapi has already done this stat() for us */
1057        if (statbuf) {
1058            struct stat *sapi_stat = sapi_get_stat(TSRMLS_C);
1059            if (!sapi_stat) {
1060                goto giveupsapistat;
1061            }
1062            *statbuf = *sapi_stat;
1063        }
1064
1065        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1066        return SUCCESS;
1067    }
1068giveupsapistat:
1069
1070    /* absolute path */
1071    if (IS_ABSOLUTE_PATH(compiler->filename, strlen(compiler->filename))) {
1072        if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
1073            return FAILURE;
1074        }
1075        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1076        return SUCCESS;
1077    }
1078
1079    /* relative path */
1080    if (*compiler->filename == '.' && (IS_SLASH(compiler->filename[1]) || compiler->filename[1] == '.')) {
1081        const char *ptr = compiler->filename + 1;
1082        if (*ptr == '.') {
1083            while (*(++ptr) == '.');
1084            if (!IS_SLASH(*ptr)) {
1085                return FAILURE;
1086            }   
1087        }
1088
1089        if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
1090            return FAILURE;
1091        }
1092
1093        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1094        return SUCCESS;
1095    }
1096
1097    return FAILURE;
1098}
1099/* }}} */
1100static int xc_entry_php_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1101{
1102    if (xc_entry_php_quick_resolve_opened_path(compiler, statbuf TSRMLS_CC) == SUCCESS) {
1103        /* opened_path resolved */
1104        return SUCCESS;
1105    }
1106    /* fall back to real stat call */
1107    else {
1108#ifdef ZEND_ENGINE_2_3
1109        char *opened_path = php_resolve_path(compiler->filename, compiler->filename_len, PG(include_path) TSRMLS_CC);
1110        if (opened_path) {
1111            strcpy(compiler->opened_path_buffer, opened_path);
1112            efree(opened_path);
1113            compiler->opened_path = compiler->opened_path_buffer;
1114            if (!statbuf || VCWD_STAT(compiler->filename, statbuf) == 0) {
1115                return SUCCESS;
1116            }
1117        }
1118#else
1119        char path_buffer[MAXPATHLEN];
1120        if (xc_resolve_path_stat(compiler->filename, path_buffer, statbuf TSRMLS_CC) == SUCCESS) {
1121            compiler->opened_path = xc_expand_url(path_buffer, compiler->opened_path_buffer TSRMLS_CC);
1122            return SUCCESS;
1123        }
1124#endif
1125    }
1126    return FAILURE;
1127}
1128/* }}} */
1129static int xc_entry_php_init_key(xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1130{
1131    if (XG(stat)) {
1132        struct stat buf;
1133        time_t delta;
1134
1135        if (compiler->opened_path) {
1136            if (VCWD_STAT(compiler->opened_path, &buf) != 0) {
1137                return FAILURE;
1138            }
1139        }
1140        else {
1141            if (xc_entry_php_resolve_opened_path(compiler, &buf TSRMLS_CC) != SUCCESS) {
1142                return FAILURE;
1143            }
1144        }
1145
1146        delta = XG(request_time) - buf.st_mtime;
1147        if (abs(delta) < 2 && !xc_test) {
1148            return FAILURE;
1149        }
1150
1151        compiler->new_entry.file_mtime   = buf.st_mtime;
1152        compiler->new_entry.file_size    = buf.st_size;
1153#ifdef HAVE_INODE
1154        compiler->new_entry.file_device  = buf.st_dev;
1155        compiler->new_entry.file_inode   = buf.st_ino;
1156#endif
1157    }
1158    else {
1159        xc_entry_php_quick_resolve_opened_path(compiler, NULL TSRMLS_CC);
1160
1161        compiler->new_entry.file_mtime   = 0;
1162        compiler->new_entry.file_size    = 0;
1163#ifdef HAVE_INODE
1164        compiler->new_entry.file_device  = 0;
1165        compiler->new_entry.file_inode   = 0;
1166#endif
1167    }
1168
1169    {
1170        xc_hash_value_t basename_hash_value;
1171        if (
1172            xc_php_hcache.size > 1
1173#ifdef HAVE_INODE
1174            || !compiler->new_entry.file_inode
1175#endif
1176            )
1177        {
1178            const char *filename_end = compiler->filename + compiler->filename_len;
1179            const char *basename = filename_end - 1;
1180
1181            /* scan till out of basename part */
1182            while (basename >= compiler->filename && !IS_SLASH(*basename)) {
1183                --basename;
1184            }
1185            /* get back to basename */
1186            ++basename;
1187
1188            basename_hash_value = HASH_STR_L(basename, filename_end - basename);
1189        }
1190
1191        compiler->entry_hash.cacheid = xc_php_hcache.size > 1 ? xc_hash_fold(basename_hash_value, &xc_php_hcache) : 0;
1192        compiler->entry_hash.entryslotid = xc_hash_fold(
1193#ifdef HAVE_INODE
1194                compiler->new_entry.file_inode
1195                ? HASH(compiler->new_entry.file_device + compiler->new_entry.file_inode)
1196                :
1197#endif
1198                basename_hash_value
1199                , &xc_php_hentry);
1200    }
1201
1202    compiler->new_entry.filepath  = NULL;
1203    compiler->new_entry.dirpath   = NULL;
1204#ifdef IS_UNICODE
1205    compiler->new_entry.ufilepath = NULL;
1206    compiler->new_entry.udirpath  = NULL;
1207#endif
1208
1209    return SUCCESS;
1210}
1211/* }}} */
1212static inline xc_hash_value_t xc_php_hash_md5(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1213{
1214    return HASH_STR_S(php->md5.digest, sizeof(php->md5.digest));
1215}
1216/* }}} */
1217static int xc_entry_data_php_init_md5(xc_cache_t *cache, xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1218{
1219    unsigned char   buf[1024];
1220    PHP_MD5_CTX     context;
1221    int             n;
1222    php_stream     *stream;
1223    ulong           old_rsid = EG(regular_list).nNextFreeElement;
1224
1225    stream = php_stream_open_wrapper((char *) compiler->filename, "rb", USE_PATH | REPORT_ERRORS | ENFORCE_SAFE_MODE | STREAM_DISABLE_OPEN_BASEDIR, NULL);
1226    if (!stream) {
1227        return FAILURE;
1228    }
1229
1230    PHP_MD5Init(&context);
1231    while ((n = php_stream_read(stream, (char *) buf, sizeof(buf))) > 0) {
1232        PHP_MD5Update(&context, buf, n);
1233    }
1234    PHP_MD5Final((unsigned char *) compiler->new_php.md5.digest, &context);
1235
1236    php_stream_close(stream);
1237    if (EG(regular_list).nNextFreeElement == old_rsid + 1) {
1238        EG(regular_list).nNextFreeElement = old_rsid;
1239    }
1240
1241    if (n < 0) {
1242        return FAILURE;
1243    }
1244
1245    compiler->new_php.hvalue = (xc_php_hash_md5(&compiler->new_php TSRMLS_CC) & cache->hphp->mask);
1246#ifdef XCACHE_DEBUG
1247    {
1248        char md5str[33];
1249        make_digest(md5str, (unsigned char *) compiler->new_php.md5.digest);
1250        TRACE("md5 %s", md5str);
1251    }
1252#endif
1253
1254    return SUCCESS;
1255}
1256/* }}} */
1257static void xc_entry_php_init(xc_entry_php_t *entry_php, const char *filepath TSRMLS_DC) /* {{{*/
1258{
1259    entry_php->filepath     = ZEND_24((char *), NOTHING) filepath;
1260    entry_php->filepath_len = strlen(entry_php->filepath);
1261    entry_php->dirpath      = estrndup(entry_php->filepath, entry_php->filepath_len);
1262    entry_php->dirpath_len  = zend_dirname(entry_php->dirpath, entry_php->filepath_len);
1263#ifdef IS_UNICODE
1264    zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->ufilepath, &entry_php->ufilepath_len, entry_php->filepath, entry_php->filepath_len TSRMLS_CC);
1265    zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->udirpath,  &entry_php->udirpath_len,  entry_php->dirpath,  entry_php->dirpath_len TSRMLS_CC);
1266#endif
1267}
1268/* }}} */
1269#ifndef ZEND_COMPILE_DELAYED_BINDING
1270static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
1271{
1272    char *class_name;
1273    zend_uint i;
1274    int class_len;
1275    xc_cest_t cest;
1276    xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
1277
1278    class_name = Z_OP_CONSTANT(opline->op1).value.str.val;
1279    class_len  = Z_OP_CONSTANT(opline->op1).value.str.len;
1280    if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
1281        assert(0);
1282    }
1283    TRACE("got ZEND_DECLARE_INHERITED_CLASS: %s", class_name + 1);
1284    /* let's see which class */
1285    for (i = 0; i < php->classinfo_cnt; i ++) {
1286        if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
1287            php->classinfos[i].oplineno = oplineno;
1288            php->have_early_binding = 1;
1289            break;
1290        }
1291    }
1292
1293    if (i == php->classinfo_cnt) {
1294        assert(0);
1295    }
1296}
1297/* }}} */
1298#endif
1299
1300/* {{{ Constant Usage */
1301#ifdef ZEND_ENGINE_2_4
1302#   define xcache_literal_is_dir  1
1303#   define xcache_literal_is_file 2
1304#else
1305#   define xcache_op1_is_file 1
1306#   define xcache_op1_is_dir  2
1307#   define xcache_op2_is_file 4
1308#   define xcache_op2_is_dir  8
1309#endif
1310typedef struct {
1311    zend_bool filepath_used;
1312    zend_bool dirpath_used;
1313    zend_bool ufilepath_used;
1314    zend_bool udirpath_used;
1315} xc_const_usage_t;
1316/* }}} */
1317static void xc_collect_op_array_info(xc_compiler_t *compiler, xc_const_usage_t *usage, xc_op_array_info_t *op_array_info, zend_op_array *op_array TSRMLS_DC) /* {{{ */
1318{
1319#ifdef ZEND_ENGINE_2_4
1320    int literalindex;
1321#else
1322    zend_uint oplinenum;
1323#endif
1324    xc_vector_t details;
1325
1326    xc_vector_init(xc_op_array_info_detail_t, &details);
1327
1328#define XCACHE_ANALYZE_LITERAL(type) \
1329    if (zend_binary_strcmp(Z_STRVAL(literal->constant), Z_STRLEN(literal->constant), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
1330        usage->type##path_used = 1; \
1331        literalinfo |= xcache_##literal##_is_##type; \
1332    }
1333
1334#define XCACHE_U_ANALYZE_LITERAL(type) \
1335    if (zend_u_##binary_strcmp(Z_USTRVAL(literal->constant), Z_USTRLEN(literal->constant), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
1336        usage->u##type##path_used = 1; \
1337        literalinfo |= xcache_##literal##_is_##type; \
1338    }
1339
1340#define XCACHE_ANALYZE_OP(type, op) \
1341    if (zend_binary_strcmp(Z_STRVAL(Z_OP_CONSTANT(opline->op)), Z_STRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
1342        usage->type##path_used = 1; \
1343        oplineinfo |= xcache_##op##_is_##type; \
1344    }
1345
1346#define XCACHE_U_ANALYZE_OP(type, op) \
1347    if (zend_u_##binary_strcmp(Z_USTRVAL(Z_OP_CONSTANT(opline->op)), Z_USTRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
1348        usage->u##type##path_used = 1; \
1349        oplineinfo |= xcache_##op##_is_##type; \
1350    }
1351
1352#ifdef ZEND_ENGINE_2_4
1353    for (literalindex = 0; literalindex < op_array->last_literal; literalindex++) {
1354        zend_literal *literal = &op_array->literals[literalindex];
1355        zend_uint literalinfo = 0;
1356        if (Z_TYPE(literal->constant) == IS_STRING) {
1357            XCACHE_ANALYZE_LITERAL(file)
1358            else XCACHE_ANALYZE_LITERAL(dir)
1359        }
1360#ifdef IS_UNICODE
1361        else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1362            XCACHE_U_ANALYZE_LITERAL(file)
1363            else XCACHE_U_ANALYZE_LITERAL(dir)
1364        }
1365#endif
1366        if (literalinfo) {
1367            xc_op_array_info_detail_t detail;
1368            detail.index = literalindex;
1369            detail.info  = literalinfo;
1370            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1371        }
1372    }
1373
1374    op_array_info->literalinfo_cnt = details.cnt;
1375    op_array_info->literalinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1376#else /* ZEND_ENGINE_2_4 */
1377    for (oplinenum = 0; oplinenum < op_array->last; oplinenum++) {
1378        zend_op *opline = &op_array->opcodes[oplinenum];
1379        zend_uint oplineinfo = 0;
1380        if (Z_OP_TYPE(opline->op1) == IS_CONST) {
1381            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1382                XCACHE_ANALYZE_OP(file, op1)
1383                else XCACHE_ANALYZE_OP(dir, op1)
1384            }
1385#ifdef IS_UNICODE
1386            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1387                XCACHE_U_ANALYZE_OP(file, op1)
1388                else XCACHE_U_ANALYZE_OP(dir, op1)
1389            }
1390#endif
1391        }
1392
1393        if (Z_OP_TYPE(opline->op2) == IS_CONST) {
1394            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1395                XCACHE_ANALYZE_OP(file, op2)
1396                else XCACHE_ANALYZE_OP(dir, op2)
1397            }
1398#ifdef IS_UNICODE
1399            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1400                XCACHE_U_ANALYZE_OP(file, op2)
1401                else XCACHE_U_ANALYZE_OP(dir, op2)
1402            }
1403#endif
1404        }
1405
1406        if (oplineinfo) {
1407            xc_op_array_info_detail_t detail;
1408            detail.index = oplinenum;
1409            detail.info  = oplineinfo;
1410            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1411        }
1412    }
1413
1414    op_array_info->oplineinfo_cnt = details.cnt;
1415    op_array_info->oplineinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1416#endif /* ZEND_ENGINE_2_4 */
1417    xc_vector_free(xc_op_array_info_detail_t, &details);
1418}
1419/* }}} */
1420void xc_fix_op_array_info(const xc_entry_php_t *entry_php, const xc_entry_data_php_t *php, zend_op_array *op_array, int shallow_copy, const xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
1421{
1422#ifdef ZEND_ENGINE_2_4
1423    zend_uint linteralindex;
1424
1425    for (linteralindex = 0; linteralindex < op_array_info->literalinfo_cnt; ++linteralindex) {
1426        int index = op_array_info->literalinfos[linteralindex].index;
1427        int literalinfo = op_array_info->literalinfos[linteralindex].info;
1428        zend_literal *literal = &op_array->literals[index];
1429        if ((literalinfo & xcache_literal_is_file)) {
1430            if (!shallow_copy) {
1431                efree(Z_STRVAL(literal->constant));
1432            }
1433            if (Z_TYPE(literal->constant) == IS_STRING) {
1434                assert(entry_php->filepath);
1435                ZVAL_STRINGL(&literal->constant, entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1436                TRACE("restored literal constant: %s", entry_php->filepath);
1437            }
1438#ifdef IS_UNICODE
1439            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1440                assert(entry_php->ufilepath);
1441                ZVAL_UNICODEL(&literal->constant, entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1442            }
1443#endif
1444            else {
1445                assert(0);
1446            }
1447        }
1448        else if ((literalinfo & xcache_literal_is_dir)) {
1449            if (!shallow_copy) {
1450                efree(Z_STRVAL(literal->constant));
1451            }
1452            if (Z_TYPE(literal->constant) == IS_STRING) {
1453                assert(entry_php->dirpath);
1454                TRACE("restored literal constant: %s", entry_php->dirpath);
1455                ZVAL_STRINGL(&literal->constant, entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1456            }
1457#ifdef IS_UNICODE
1458            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1459                assert(!entry_php->udirpath);
1460                ZVAL_UNICODEL(&literal->constant, entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1461            }
1462#endif
1463            else {
1464                assert(0);
1465            }
1466        }
1467    }
1468#else /* ZEND_ENGINE_2_4 */
1469    zend_uint oplinenum;
1470
1471    for (oplinenum = 0; oplinenum < op_array_info->oplineinfo_cnt; ++oplinenum) {
1472        int oplineno = op_array_info->oplineinfos[oplinenum].index;
1473        int oplineinfo = op_array_info->oplineinfos[oplinenum].info;
1474        zend_op *opline = &op_array->opcodes[oplineno];
1475        if ((oplineinfo & xcache_op1_is_file)) {
1476            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1477            if (!shallow_copy) {
1478                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1479            }
1480            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1481                assert(entry_php->filepath);
1482                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1483                TRACE("restored op1 constant: %s", entry_php->filepath);
1484            }
1485#ifdef IS_UNICODE
1486            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1487                assert(entry_php->ufilepath);
1488                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1489            }
1490#endif
1491            else {
1492                assert(0);
1493            }
1494        }
1495        else if ((oplineinfo & xcache_op1_is_dir)) {
1496            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1497            if (!shallow_copy) {
1498                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1499            }
1500            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1501                assert(entry_php->dirpath);
1502                TRACE("restored op1 constant: %s", entry_php->dirpath);
1503                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1504            }
1505#ifdef IS_UNICODE
1506            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1507                assert(!entry_php->udirpath);
1508                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1509            }
1510#endif
1511            else {
1512                assert(0);
1513            }
1514        }
1515
1516        if ((oplineinfo & xcache_op2_is_file)) {
1517            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1518            if (!shallow_copy) {
1519                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1520            }
1521            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1522                assert(entry_php->filepath);
1523                TRACE("restored op2 constant: %s", entry_php->filepath);
1524                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1525            }
1526#ifdef IS_UNICODE
1527            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1528                assert(entry_php->ufilepath);
1529                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1530            }
1531#endif
1532            else {
1533                assert(0);
1534            }
1535        }
1536        else if ((oplineinfo & xcache_op2_is_dir)) {
1537            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1538            if (!shallow_copy) {
1539                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1540            }
1541            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1542                assert(!entry_php->dirpath);
1543                TRACE("restored op2 constant: %s", entry_php->dirpath);
1544                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1545            }
1546#ifdef IS_UNICODE
1547            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1548                assert(!entry_php->udirpath);
1549                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1550            }
1551#endif
1552            else {
1553                assert(0);
1554            }
1555        }
1556    }
1557#endif /* ZEND_ENGINE_2_4 */
1558}
1559/* }}} */
1560static void xc_free_op_array_info(xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
1561{
1562#ifdef ZEND_ENGINE_2_4
1563    if (op_array_info->literalinfos) {
1564        efree(op_array_info->literalinfos);
1565    }
1566#else
1567    if (op_array_info->oplineinfos) {
1568        efree(op_array_info->oplineinfos);
1569    }
1570#endif
1571}
1572/* }}} */
1573static void xc_free_php(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1574{
1575    zend_uint i;
1576    if (php->classinfos) {
1577        for (i = 0; i < php->classinfo_cnt; i ++) {
1578            xc_classinfo_t *classinfo = &php->classinfos[i];
1579            zend_uint j;
1580
1581            for (j = 0; j < classinfo->methodinfo_cnt; j ++) {
1582                xc_free_op_array_info(&classinfo->methodinfos[j] TSRMLS_CC);
1583            }
1584
1585            if (classinfo->methodinfos) {
1586                efree(classinfo->methodinfos);
1587            }
1588        }
1589    }
1590    if (php->funcinfos) {
1591        for (i = 0; i < php->funcinfo_cnt; i ++) {
1592            xc_free_op_array_info(&php->funcinfos[i].op_array_info TSRMLS_CC);
1593        }
1594    }
1595    xc_free_op_array_info(&php->op_array_info TSRMLS_CC);
1596
1597#define X_FREE(var) do {\
1598    if (php->var) { \
1599        efree(php->var); \
1600    } \
1601} while (0)
1602
1603#ifdef ZEND_ENGINE_2_1
1604    X_FREE(autoglobals);
1605#endif
1606    X_FREE(classinfos);
1607    X_FREE(funcinfos);
1608#ifdef HAVE_XCACHE_CONSTANT
1609    X_FREE(constinfos);
1610#endif
1611#undef X_FREE
1612}
1613/* }}} */
1614static void xc_compile_php(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
1615{
1616    zend_uint old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
1617    zend_bool catched = 0;
1618
1619    /* {{{ compile */
1620    TRACE("compiling %s", h->opened_path ? h->opened_path : h->filename);
1621
1622    old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
1623    old_funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
1624    old_constinfo_cnt = zend_hash_num_elements(EG(zend_constants));
1625
1626    zend_try {
1627        compiler->new_php.op_array = old_compile_file(h, type TSRMLS_CC);
1628    } zend_catch {
1629        catched = 1;
1630    } zend_end_try();
1631
1632    if (catched) {
1633        goto err_bailout;
1634    }
1635
1636    if (compiler->new_php.op_array == NULL) {
1637        goto err_op_array;
1638    }
1639
1640    if (!XG(initial_compile_file_called)) {
1641        return;
1642    }
1643
1644    /* }}} */
1645    /* {{{ prepare */
1646    zend_restore_compiled_filename(h->opened_path ? h->opened_path : (char *) h->filename TSRMLS_CC);
1647
1648#ifdef HAVE_XCACHE_CONSTANT
1649    compiler->new_php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
1650#endif
1651    compiler->new_php.funcinfo_cnt   = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
1652    compiler->new_php.classinfo_cnt  = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
1653#ifdef ZEND_ENGINE_2_1
1654    /* {{{ count new_php.autoglobal_cnt */ {
1655        Bucket *b;
1656
1657        compiler->new_php.autoglobal_cnt = 0;
1658        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1659            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1660            /* check if actived */
1661            if (auto_global->auto_global_callback && !auto_global->armed) {
1662                compiler->new_php.autoglobal_cnt ++;
1663            }
1664        }
1665    }
1666    /* }}} */
1667#endif
1668
1669#define X_ALLOC_N(var, cnt) do {     \
1670    if (compiler->new_php.cnt) {                  \
1671        ECALLOC_N(compiler->new_php.var, compiler->new_php.cnt); \
1672        if (!compiler->new_php.var) {             \
1673            goto err_alloc;          \
1674        }                            \
1675    }                                \
1676    else {                           \
1677        compiler->new_php.var = NULL;             \
1678    }                                \
1679} while (0)
1680
1681#ifdef HAVE_XCACHE_CONSTANT
1682    X_ALLOC_N(constinfos,  constinfo_cnt);
1683#endif
1684    X_ALLOC_N(funcinfos,   funcinfo_cnt);
1685    X_ALLOC_N(classinfos,  classinfo_cnt);
1686#ifdef ZEND_ENGINE_2_1
1687    X_ALLOC_N(autoglobals, autoglobal_cnt);
1688#endif
1689#undef X_ALLOC
1690    /* }}} */
1691
1692    /* {{{ shallow copy, pointers only */ {
1693        Bucket *b;
1694        zend_uint i;
1695        zend_uint j;
1696
1697#define COPY_H(vartype, var, cnt, name, datatype) do {        \
1698    for (i = 0, j = 0; b; i ++, b = b->pListNext) {           \
1699        vartype *data = &compiler->new_php.var[j];                         \
1700                                                              \
1701        if (i < old_##cnt) {                                  \
1702            continue;                                         \
1703        }                                                     \
1704        j ++;                                                 \
1705                                                              \
1706        assert(i < old_##cnt + compiler->new_php.cnt);                     \
1707        assert(b->pData);                                     \
1708        memcpy(&data->name, b->pData, sizeof(datatype));      \
1709        UNISW(NOTHING, data->type = b->key.type;)             \
1710        if (UNISW(1, b->key.type == IS_STRING)) {             \
1711            ZSTR_S(data->key)      = BUCKET_KEY_S(b);         \
1712        }                                                     \
1713        else {                                                \
1714            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
1715        }                                                     \
1716        data->key_size   = b->nKeyLength;                     \
1717        data->h          = b->h;                              \
1718    }                                                         \
1719} while(0)
1720
1721#ifdef HAVE_XCACHE_CONSTANT
1722        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1723#endif
1724        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1725        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1726
1727#undef COPY_H
1728
1729        /* for ZE1, cest need to be fixed inside store */
1730
1731#ifdef ZEND_ENGINE_2_1
1732        /* scan for acatived auto globals */
1733        i = 0;
1734        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1735            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1736            /* check if actived */
1737            if (auto_global->auto_global_callback && !auto_global->armed) {
1738                xc_autoglobal_t *data = &compiler->new_php.autoglobals[i];
1739
1740                assert(i < compiler->new_php.autoglobal_cnt);
1741                i ++;
1742                UNISW(NOTHING, data->type = b->key.type;)
1743                if (UNISW(1, b->key.type == IS_STRING)) {
1744                    ZSTR_S(data->key)     = BUCKET_KEY_S(b);
1745                }
1746                else {
1747                    ZSTR_U(data->key)     = BUCKET_KEY_U(b);
1748                }
1749                data->key_len = b->nKeyLength - 1;
1750                data->h       = b->h;
1751            }
1752        }
1753#endif
1754    }
1755    /* }}} */
1756
1757    /* {{{ collect info for file/dir path */ {
1758        Bucket *b;
1759        xc_const_usage_t const_usage;
1760        unsigned int i;
1761
1762        xc_entry_php_init(&compiler->new_entry, zend_get_compiled_filename(TSRMLS_C) TSRMLS_CC);
1763        memset(&const_usage, 0, sizeof(const_usage));
1764
1765        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1766            xc_classinfo_t *classinfo = &compiler->new_php.classinfos[i];
1767            zend_class_entry *ce = CestToCePtr(classinfo->cest);
1768            classinfo->methodinfo_cnt = ce->function_table.nTableSize;
1769            if (classinfo->methodinfo_cnt) {
1770                int j;
1771
1772                ECALLOC_N(classinfo->methodinfos, classinfo->methodinfo_cnt);
1773                if (!classinfo->methodinfos) {
1774                    goto err_alloc;
1775                }
1776
1777                for (j = 0, b = ce->function_table.pListHead; b; j ++, b = b->pListNext) {
1778                    xc_collect_op_array_info(compiler, &const_usage, &classinfo->methodinfos[j], (zend_op_array *) b->pData TSRMLS_CC);
1779                }
1780            }
1781            else {
1782                classinfo->methodinfos = NULL;
1783            }
1784        }
1785
1786        for (i = 0; i < compiler->new_php.funcinfo_cnt; i ++) {
1787            xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.funcinfos[i].op_array_info, (zend_op_array *) &compiler->new_php.funcinfos[i].func TSRMLS_CC);
1788        }
1789
1790        xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.op_array_info, compiler->new_php.op_array TSRMLS_CC);
1791
1792        /* file/dir path free unused */
1793#define X_FREE_UNUSED(var) \
1794        if (!const_usage.var##path_used) { \
1795            efree(compiler->new_entry.var##path); \
1796            compiler->new_entry.var##path = NULL; \
1797            compiler->new_entry.var##path_len = 0; \
1798        }
1799        /* filepath is required to restore op_array->filename, so no free filepath here */
1800        X_FREE_UNUSED(dir)
1801#ifdef IS_UNICODE
1802        X_FREE_UNUSED(ufile)
1803        X_FREE_UNUSED(udir)
1804#endif
1805#undef X_FREE_UNUSED
1806    }
1807    /* }}} */
1808#ifdef XCACHE_ERROR_CACHING
1809    compiler->new_php.compilererrors = xc_sandbox_compilererrors(TSRMLS_C);
1810    compiler->new_php.compilererror_cnt = xc_sandbox_compilererror_cnt(TSRMLS_C);
1811#endif
1812#ifndef ZEND_COMPILE_DELAYED_BINDING
1813    /* {{{ find inherited classes that should be early-binding */
1814    compiler->new_php.have_early_binding = 0;
1815    {
1816        zend_uint i;
1817        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1818            compiler->new_php.classinfos[i].oplineno = -1;
1819        }
1820    }
1821
1822    xc_undo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1823    xc_foreach_early_binding_class(compiler->new_php.op_array, xc_cache_early_binding_class_cb, (void *) &compiler->new_php TSRMLS_CC);
1824    xc_redo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1825    /* }}} */
1826#endif
1827
1828    return;
1829
1830err_alloc:
1831    xc_free_php(&compiler->new_php TSRMLS_CC);
1832
1833err_bailout:
1834err_op_array:
1835
1836    if (catched) {
1837        zend_bailout();
1838    }
1839}
1840/* }}} */
1841static zend_op_array *xc_compile_restore(xc_entry_php_t *stored_entry, xc_entry_data_php_t *stored_php TSRMLS_DC) /* {{{ */
1842{
1843    zend_op_array *op_array;
1844    xc_entry_php_t restored_entry;
1845    xc_entry_data_php_t restored_php;
1846    zend_bool catched;
1847
1848    CG(in_compilation)    = 1;
1849    CG(compiled_filename) = stored_entry->entry.name.str.val;
1850    CG(zend_lineno)       = 0;
1851    TRACE("restoring %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1852    xc_processor_restore_xc_entry_php_t(&restored_entry, stored_entry TSRMLS_CC);
1853    xc_processor_restore_xc_entry_data_php_t(stored_entry, &restored_php, stored_php, xc_readonly_protection TSRMLS_CC);
1854    restored_entry.php = &restored_php;
1855#ifdef SHOW_DPRINT
1856    xc_dprint(&restored_entry, 0 TSRMLS_CC);
1857#endif
1858
1859    catched = 0;
1860    zend_try {
1861        op_array = xc_entry_install(&restored_entry TSRMLS_CC);
1862    } zend_catch {
1863        catched = 1;
1864    } zend_end_try();
1865
1866#ifdef HAVE_XCACHE_CONSTANT
1867    if (restored_php.constinfos) {
1868        efree(restored_php.constinfos);
1869    }
1870#endif
1871    if (restored_php.funcinfos) {
1872        efree(restored_php.funcinfos);
1873    }
1874    if (restored_php.classinfos) {
1875        efree(restored_php.classinfos);
1876    }
1877
1878    if (catched) {
1879        zend_bailout();
1880    }
1881    CG(in_compilation)    = 0;
1882    CG(compiled_filename) = NULL;
1883    TRACE("restored %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1884    return op_array;
1885}
1886/* }}} */
1887static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
1888{
1889    XG(initial_compile_file_called) = 1;
1890    return origin_compile_file(h, type TSRMLS_CC);
1891}
1892/* }}} */
1893typedef struct xc_sandboxed_compiler_t { /* {{{ */
1894    xc_compiler_t *compiler;
1895    /* input */
1896    zend_file_handle *h;
1897    int type;
1898
1899    /* sandbox output */
1900    xc_entry_php_t *stored_entry;
1901    xc_entry_data_php_t *stored_php;
1902} xc_sandboxed_compiler_t; /* {{{ */
1903
1904static zend_op_array *xc_compile_file_sandboxed(void *data TSRMLS_DC) /* {{{ */
1905{
1906    xc_sandboxed_compiler_t *sandboxed_compiler = (xc_sandboxed_compiler_t *) data;
1907    xc_compiler_t *compiler = sandboxed_compiler->compiler;
1908    zend_bool catched = 0;
1909    xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
1910    xc_entry_php_t *stored_entry;
1911    xc_entry_data_php_t *stored_php;
1912
1913    /* {{{ compile */
1914    /* make compile inside sandbox */
1915#ifdef HAVE_XCACHE_CONSTANT
1916    compiler->new_php.constinfos  = NULL;
1917#endif
1918    compiler->new_php.funcinfos   = NULL;
1919    compiler->new_php.classinfos  = NULL;
1920#ifdef ZEND_ENGINE_2_1
1921    compiler->new_php.autoglobals = NULL;
1922#endif
1923    memset(&compiler->new_php.op_array_info, 0, sizeof(compiler->new_php.op_array_info));
1924
1925    XG(initial_compile_file_called) = 0;
1926    zend_try {
1927        compiler->new_php.op_array = NULL;
1928        xc_compile_php(compiler, sandboxed_compiler->h, sandboxed_compiler->type TSRMLS_CC);
1929    } zend_catch {
1930        catched = 1;
1931    } zend_end_try();
1932
1933    if (catched
1934     || !compiler->new_php.op_array /* possible ? */
1935     || !XG(initial_compile_file_called)) {
1936        goto err_aftersandbox;
1937    }
1938
1939    /* }}} */
1940#ifdef SHOW_DPRINT
1941    compiler->new_entry.php = &compiler->new_php;
1942    xc_dprint(&compiler->new_entry, 0 TSRMLS_CC);
1943#endif
1944
1945    stored_entry = NULL;
1946    stored_php = NULL;
1947    ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
1948        /* php_store */
1949        stored_php = xc_php_store_unlocked(cache, &compiler->new_php TSRMLS_CC);
1950        if (!stored_php) {
1951            /* error */
1952            break;
1953        }
1954        /* entry_store */
1955        compiler->new_entry.php = stored_php;
1956        stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
1957        if (stored_entry) {
1958            xc_php_addref_unlocked(stored_php);
1959            TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
1960            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
1961        }
1962    } LEAVE_LOCK_EX(cache);
1963    /* }}} */
1964    TRACE("%s", stored_entry ? "stored" : "store failed");
1965
1966    if (catched || !stored_php) {
1967        goto err_aftersandbox;
1968    }
1969
1970    cache->compiling = 0;
1971    xc_free_php(&compiler->new_php TSRMLS_CC);
1972
1973    if (stored_entry) {
1974        if (compiler->new_php.op_array) {
1975#ifdef ZEND_ENGINE_2
1976            destroy_op_array(compiler->new_php.op_array TSRMLS_CC);
1977#else
1978            destroy_op_array(compiler->new_php.op_array);
1979#endif
1980            efree(compiler->new_php.op_array);
1981            compiler->new_php.op_array = NULL;
1982            sandboxed_compiler->h = NULL;
1983        }
1984        sandboxed_compiler->stored_entry = stored_entry;
1985        sandboxed_compiler->stored_php = stored_php;
1986        /* sandbox no install */
1987        return NULL;
1988    }
1989    else {
1990        /* install it with sandbox */
1991        return compiler->new_php.op_array;
1992    }
1993
1994err_aftersandbox:
1995    xc_free_php(&compiler->new_php TSRMLS_CC);
1996
1997    cache->compiling = 0;
1998    if (catched) {
1999        cache->errors ++;
2000        zend_bailout();
2001    }
2002    return compiler->new_php.op_array;
2003} /* }}} */
2004static zend_op_array *xc_compile_file_cached(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2005{
2006    /*
2007    if (clog) {
2008        return old;
2009    }
2010
2011    if (cached_entry = getby entry_hash) {
2012        php = cached_entry.php;
2013        php = restore(php);
2014        return php;
2015    }
2016    else {
2017        if (!(php = getby md5)) {
2018            if (clog) {
2019                return old;
2020            }
2021
2022            inside_sandbox {
2023                php = compile;
2024                entry = create entries[entry];
2025            }
2026        }
2027
2028        entry.php = php;
2029        return php;
2030    }
2031    */
2032
2033    xc_entry_php_t *stored_entry;
2034    xc_entry_data_php_t *stored_php;
2035    zend_bool gaveup = 0;
2036    zend_bool catched = 0;
2037    zend_op_array *op_array;
2038    xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
2039    xc_sandboxed_compiler_t sandboxed_compiler;
2040
2041    /* stale clogs precheck */
2042    if (XG(request_time) - cache->compiling < 30) {
2043        cache->clogs ++;
2044        return old_compile_file(h, type TSRMLS_CC);
2045    }
2046
2047    /* {{{ entry_lookup/hit/md5_init/php_lookup */
2048    stored_entry = NULL;
2049    stored_php = NULL;
2050
2051    ENTER_LOCK_EX(cache) {
2052        if (!compiler->opened_path && xc_entry_resolve_path_unlocked(compiler, compiler->filename, &stored_entry TSRMLS_CC) == SUCCESS) {
2053            compiler->opened_path = compiler->new_entry.entry.name.str.val;
2054        }
2055        else {
2056            if (!compiler->opened_path && xc_entry_php_resolve_opened_path(compiler, NULL TSRMLS_CC) != SUCCESS) {
2057                gaveup = 1;
2058                break;
2059            }
2060
2061            /* finalize name */
2062            compiler->new_entry.entry.name.str.val = (char *) compiler->opened_path;
2063            compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
2064
2065            stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(XC_TYPE_PHP, cache, compiler->entry_hash.entryslotid, (xc_entry_t *) &compiler->new_entry TSRMLS_CC);
2066        }
2067
2068        if (stored_entry) {
2069            xc_cache_hit_unlocked(cache TSRMLS_CC);
2070
2071            TRACE(" hit %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2072            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2073            stored_php = stored_entry->php;
2074            break;
2075        }
2076
2077        TRACE("miss entry %d:%s", compiler->new_entry.file_inode, compiler->new_entry.entry.name.str.val);
2078
2079        if (xc_entry_data_php_init_md5(cache, compiler TSRMLS_CC) != SUCCESS) {
2080            gaveup = 1;
2081            break;
2082        }
2083
2084        stored_php = xc_php_find_unlocked(cache, &compiler->new_php TSRMLS_CC);
2085
2086        if (stored_php) {
2087            compiler->new_entry.php = stored_php;
2088            xc_entry_php_init(&compiler->new_entry, compiler->opened_path TSRMLS_CC);
2089            stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
2090            if (stored_entry) {
2091                xc_php_addref_unlocked(stored_php);
2092                TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2093                xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2094            }
2095            else {
2096                gaveup = 1;
2097            }
2098            break;
2099        }
2100
2101        if (XG(request_time) - cache->compiling < 30) {
2102            TRACE("%s", "miss php, but compiling");
2103            cache->clogs ++;
2104            gaveup = 1;
2105            break;
2106        }
2107
2108        TRACE("%s", "miss php, going to compile");
2109        cache->compiling = XG(request_time);
2110    } LEAVE_LOCK_EX(cache);
2111
2112    if (catched) {
2113        cache->compiling = 0;
2114        zend_bailout();
2115    }
2116
2117    /* found entry */
2118    if (stored_entry && stored_php) {
2119        return xc_compile_restore(stored_entry, stored_php TSRMLS_CC);
2120    }
2121
2122    /* gaveup */
2123    if (gaveup) {
2124        return old_compile_file(h, type TSRMLS_CC);
2125    }
2126    /* }}} */
2127
2128    sandboxed_compiler.compiler = compiler;
2129    sandboxed_compiler.h = h;
2130    sandboxed_compiler.type = type;
2131    sandboxed_compiler.stored_php = NULL;
2132    sandboxed_compiler.stored_entry = NULL;
2133    op_array = xc_sandbox(xc_compile_file_sandboxed, (void *) &sandboxed_compiler, h->opened_path ? h->opened_path : h->filename TSRMLS_CC);
2134    if (sandboxed_compiler.stored_entry) {
2135        return xc_compile_restore(sandboxed_compiler.stored_entry, sandboxed_compiler.stored_php TSRMLS_CC);
2136    }
2137    else {
2138        return op_array;
2139    }
2140}
2141/* }}} */
2142static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2143{
2144    xc_compiler_t compiler;
2145    zend_op_array *op_array;
2146
2147    assert(xc_initized);
2148
2149    TRACE("xc_compile_file: type=%d name=%s", h->type, h->filename ? h->filename : "NULL");
2150
2151    if (!XG(cacher)
2152     || !h->filename
2153     || !SG(request_info).path_translated
2154     || strstr(h->filename, "://") != NULL
2155#ifdef ZEND_ENGINE_2_3
2156     /* supported by php_resolve_path */
2157     || (!XG(stat) && strstr(PG(include_path), "://") != NULL)
2158#else
2159     || strstr(PG(include_path), "://") != NULL
2160#endif
2161     ) {
2162        op_array = old_compile_file(h, type TSRMLS_CC);
2163        TRACE("%s", "cacher not enabled");
2164        return op_array;
2165    }
2166
2167    /* {{{ entry_init_key */
2168    compiler.opened_path = h->opened_path;
2169    compiler.filename = compiler.opened_path ? compiler.opened_path : h->filename;
2170    compiler.filename_len = strlen(compiler.filename);
2171    if (xc_entry_php_init_key(&compiler TSRMLS_CC) != SUCCESS) {
2172        TRACE("failed to init key for %s", compiler.filename);
2173        return old_compile_file(h, type TSRMLS_CC);
2174    }
2175    /* }}} */
2176
2177    op_array = xc_compile_file_cached(&compiler, h, type TSRMLS_CC);
2178
2179    xc_entry_free_key_php(&compiler.new_entry TSRMLS_CC);
2180
2181    return op_array;
2182}
2183/* }}} */
2184
2185/* gdb helper functions, but N/A for coredump */
2186int xc_is_rw(const void *p) /* {{{ */
2187{
2188    xc_shm_t *shm;
2189    size_t i;
2190
2191    if (xc_php_caches) {
2192        for (i = 0; i < xc_php_hcache.size; i ++) {
2193            shm = xc_php_caches[i]->shm;
2194            if (shm->handlers->is_readwrite(shm, p)) {
2195                return 1;
2196            }
2197        }
2198    }
2199
2200    if (xc_var_caches) {
2201        for (i = 0; i < xc_var_hcache.size; i ++) {
2202            shm = xc_var_caches[i]->shm;
2203            if (shm->handlers->is_readwrite(shm, p)) {
2204                return 1;
2205            }
2206        }
2207    }
2208    return 0;
2209}
2210/* }}} */
2211int xc_is_ro(const void *p) /* {{{ */
2212{
2213    xc_shm_t *shm;
2214    size_t i;
2215
2216    if (xc_php_caches) {
2217        for (i = 0; i < xc_php_hcache.size; i ++) {
2218            shm = xc_php_caches[i]->shm;
2219            if (shm->handlers->is_readonly(shm, p)) {
2220                return 1;
2221            }
2222        }
2223    }
2224
2225    if (xc_var_caches) {
2226        for (i = 0; i < xc_var_hcache.size; i ++) {
2227            shm = xc_var_caches[i]->shm;
2228            if (shm->handlers->is_readonly(shm, p)) {
2229                return 1;
2230            }
2231        }
2232    }
2233    return 0;
2234}
2235/* }}} */
2236int xc_is_shm(const void *p) /* {{{ */
2237{
2238    return xc_is_ro(p) || xc_is_rw(p);
2239}
2240/* }}} */
2241
2242void xc_gc_add_op_array(xc_gc_op_array_t *gc_op_array TSRMLS_DC) /* {{{ */
2243{
2244    zend_llist_add_element(&XG(gc_op_arrays), (void *) gc_op_array);
2245}
2246/* }}} */
2247static void xc_gc_op_array(void *pDest) /* {{{ */
2248{
2249    xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
2250    zend_uint i;
2251#ifdef ZEND_ENGINE_2
2252    if (op_array->arg_info) {
2253        for (i = 0; i < op_array->num_args; i++) {
2254            efree((char *) ZSTR_V(op_array->arg_info[i].name));
2255            if (ZSTR_V(op_array->arg_info[i].class_name)) {
2256                efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
2257            }
2258        }
2259        efree(op_array->arg_info);
2260    }
2261#endif
2262    if (op_array->opcodes) {
2263        efree(op_array->opcodes);
2264    }
2265}
2266/* }}} */
2267
2268/* module helper function */
2269static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
2270{
2271    typedef struct {
2272        const char *prefix;
2273        zend_uchar (*getsize)();
2274        const char *(*get)(zend_uchar i);
2275    } xc_meminfo_t;
2276    xc_meminfo_t nameinfos[] = {
2277        { "",        xc_get_op_type_count,   xc_get_op_type   },
2278        { "",        xc_get_data_type_count, xc_get_data_type },
2279        { "",        xc_get_opcode_count,    xc_get_opcode    },
2280        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
2281        { NULL, NULL, NULL }
2282    };
2283    xc_meminfo_t* p;
2284    zend_uchar i, count;
2285    char const_name[96];
2286    int const_name_len;
2287    int undefdone = 0;
2288
2289    for (p = nameinfos; p->getsize; p ++) {
2290        count = p->getsize();
2291        for (i = 0; i < count; i ++) {
2292            const char *name = p->get(i);
2293            if (!name) continue;
2294            if (strcmp(name, "UNDEF") == 0) {
2295                if (undefdone) continue;
2296                undefdone = 1;
2297            }
2298            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
2299            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2300        }
2301    }
2302
2303    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2304    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2305    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2306    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2307    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2308    return 0;
2309}
2310/* }}} */
2311static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
2312{
2313    size_t i;
2314    xc_cache_t *cache;
2315    xc_shm_t *shm;
2316
2317    if (!caches) {
2318        return NULL;
2319    }
2320    shm = NULL;
2321    for (i = 0; i < hcache->size; i ++) {
2322        cache = caches[i];
2323        if (cache) {
2324            if (cache->lck) {
2325                xc_lock_destroy(cache->lck);
2326            }
2327            /* do NOT free
2328            if (cache->entries) {
2329                cache->mem->handlers->free(cache->mem, cache->entries);
2330            }
2331            cache->mem->handlers->free(cache->mem, cache);
2332            */
2333            shm = cache->shm;
2334            shm->handlers->memdestroy(cache->mem);
2335        }
2336    }
2337    free(caches);
2338    return shm;
2339}
2340/* }}} */
2341static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_hash_t *hphp, xc_shmsize_t shmsize) /* {{{ */
2342{
2343    xc_cache_t **caches = NULL, *cache;
2344    xc_mem_t *mem;
2345    time_t now = time(NULL);
2346    size_t i;
2347    xc_memsize_t memsize;
2348
2349    memsize = shmsize / hcache->size;
2350
2351    /* Don't let it break out of mem after ALIGNed
2352     * This is important for
2353     * Simply loop until it fit our need
2354     */
2355    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
2356        if (memsize < ALIGN(1)) {
2357            CHECK(NULL, "cache too small");
2358        }
2359        memsize --;
2360    }
2361
2362    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
2363
2364    for (i = 0; i < hcache->size; i ++) {
2365        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
2366        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
2367        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
2368        if (hphp) {
2369            CHECK(cache->phps= mem->handlers->calloc(mem, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
2370        }
2371        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
2372
2373        cache->hcache  = hcache;
2374        cache->hentry  = hentry;
2375        cache->hphp    = hphp;
2376        cache->shm     = shm;
2377        cache->mem     = mem;
2378        cache->cacheid = i;
2379        cache->last_gc_deletes = now;
2380        cache->last_gc_expires = now;
2381        caches[i] = cache;
2382    }
2383    return caches;
2384
2385err:
2386    if (caches) {
2387        xc_cache_destroy(caches, hcache);
2388    }
2389    return NULL;
2390}
2391/* }}} */
2392static void xc_destroy() /* {{{ */
2393{
2394    xc_shm_t *shm = NULL;
2395
2396    if (old_compile_file) {
2397        zend_compile_file = old_compile_file;
2398        old_compile_file = NULL;
2399    }
2400
2401    if (origin_compile_file) {
2402        zend_compile_file = origin_compile_file;
2403        origin_compile_file = NULL;
2404    }
2405
2406    if (xc_php_caches) {
2407        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
2408        xc_php_caches = NULL;
2409    }
2410
2411    if (xc_var_caches) {
2412        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
2413        xc_var_caches = NULL;
2414    }
2415
2416    if (shm) {
2417        xc_shm_destroy(shm);
2418    }
2419
2420    xc_initized = 0;
2421}
2422/* }}} */
2423static int xc_init(int module_number TSRMLS_DC) /* {{{ */
2424{
2425    xc_shm_t *shm;
2426    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
2427
2428    xc_php_caches = xc_var_caches = NULL;
2429    shm = NULL;
2430
2431    if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
2432        zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
2433        goto err;
2434    }
2435
2436    if (xc_php_size || xc_var_size) {
2437        CHECK(shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
2438        if (!shm->handlers->can_readonly(shm)) {
2439            xc_readonly_protection = 0;
2440        }
2441
2442        if (xc_php_size) {
2443            old_compile_file = zend_compile_file;
2444            zend_compile_file = xc_compile_file;
2445
2446            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, &xc_php_hentry, xc_php_size), "failed init opcode cache");
2447        }
2448
2449        if (xc_var_size) {
2450            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, NULL, xc_var_size), "failed init variable cache");
2451        }
2452    }
2453    return SUCCESS;
2454
2455err:
2456    if (xc_php_caches || xc_var_caches) {
2457        xc_destroy();
2458        /* shm destroied in xc_destroy() */
2459    }
2460    else if (shm) {
2461        xc_destroy();
2462        xc_shm_destroy(shm);
2463    }
2464    return 0;
2465}
2466/* }}} */
2467static void xc_request_init(TSRMLS_D) /* {{{ */
2468{
2469    size_t i;
2470
2471    if (!XG(internal_table_copied)) {
2472        zend_function tmp_func;
2473        xc_cest_t tmp_cest;
2474
2475#ifdef HAVE_XCACHE_CONSTANT
2476        zend_hash_destroy(&XG(internal_constant_table));
2477#endif
2478        zend_hash_destroy(&XG(internal_function_table));
2479        zend_hash_destroy(&XG(internal_class_table));
2480
2481#ifdef HAVE_XCACHE_CONSTANT
2482        zend_hash_init_ex(&XG(internal_constant_table), 20,  NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2483#endif
2484        zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
2485        zend_hash_init_ex(&XG(internal_class_table),    10,  NULL, NULL, 1, 0);
2486
2487#ifdef HAVE_XCACHE_CONSTANT
2488        xc_copy_internal_zend_constants(&XG(internal_constant_table), EG(zend_constants));
2489#endif
2490        zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
2491        zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
2492
2493        XG(internal_table_copied) = 1;
2494    }
2495    if (xc_php_caches && !XG(php_holds)) {
2496        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
2497        for (i = 0; i < xc_php_hcache.size; i ++) {
2498            xc_stack_init(&XG(php_holds[i]));
2499        }
2500    }
2501
2502    if (xc_var_caches && !XG(var_holds)) {
2503        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
2504        for (i = 0; i < xc_var_hcache.size; i ++) {
2505            xc_stack_init(&XG(var_holds[i]));
2506        }
2507    }
2508
2509#ifdef ZEND_ENGINE_2
2510    zend_llist_init(&XG(gc_op_arrays), sizeof(xc_gc_op_array_t), xc_gc_op_array, 0);
2511#endif
2512
2513#if PHP_API_VERSION <= 20041225
2514    XG(request_time) = time(NULL);
2515#else
2516    XG(request_time) = sapi_get_request_time(TSRMLS_C);
2517#endif
2518
2519#ifdef HAVE_XCACHE_COVERAGER
2520    xc_coverager_request_init(TSRMLS_C);
2521#endif
2522}
2523/* }}} */
2524static void xc_request_shutdown(TSRMLS_D) /* {{{ */
2525{
2526    xc_entry_unholds(TSRMLS_C);
2527#ifdef ZEND_ENGINE_2
2528    zend_llist_destroy(&XG(gc_op_arrays));
2529#endif
2530    xc_gc_expires_php(TSRMLS_C);
2531    xc_gc_expires_var(TSRMLS_C);
2532    xc_gc_deletes(TSRMLS_C);
2533#ifdef HAVE_XCACHE_COVERAGER
2534    xc_coverager_request_shutdown(TSRMLS_C);
2535#endif
2536}
2537/* }}} */
2538/* {{{ PHP_GINIT_FUNCTION(xcache) */
2539static
2540#ifdef PHP_GINIT_FUNCTION
2541PHP_GINIT_FUNCTION(xcache)
2542#else
2543void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
2544#endif
2545{
2546    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
2547
2548#ifdef HAVE_XCACHE_CONSTANT
2549    zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2550#endif
2551    zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
2552    zend_hash_init_ex(&xcache_globals->internal_class_table,    1, NULL, NULL, 1, 0);
2553}
2554/* }}} */
2555/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
2556static
2557#ifdef PHP_GSHUTDOWN_FUNCTION
2558PHP_GSHUTDOWN_FUNCTION(xcache)
2559#else
2560void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
2561#endif
2562{
2563    size_t i;
2564
2565    if (xcache_globals->php_holds != NULL) {
2566        for (i = 0; i < xc_php_hcache.size; i ++) {
2567            xc_stack_destroy(&xcache_globals->php_holds[i]);
2568        }
2569        free(xcache_globals->php_holds);
2570        xcache_globals->php_holds = NULL;
2571    }
2572
2573    if (xcache_globals->var_holds != NULL) {
2574        for (i = 0; i < xc_var_hcache.size; i ++) {
2575            xc_stack_destroy(&xcache_globals->var_holds[i]);
2576        }
2577        free(xcache_globals->var_holds);
2578        xcache_globals->var_holds = NULL;
2579    }
2580
2581    if (xcache_globals->internal_table_copied) {
2582#ifdef HAVE_XCACHE_CONSTANT
2583        zend_hash_destroy(&xcache_globals->internal_constant_table);
2584#endif
2585        zend_hash_destroy(&xcache_globals->internal_function_table);
2586        zend_hash_destroy(&xcache_globals->internal_class_table);
2587    }
2588}
2589/* }}} */
2590
2591/* user functions */
2592static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
2593{
2594    zval **server = NULL;
2595    zval **user = NULL;
2596    zval **pass = NULL;
2597    char *admin_user = NULL;
2598    char *admin_pass = NULL;
2599    HashTable *ht;
2600
2601    /* auth disabled, nothing to do.. */
2602    if (!XG(auth_enabled)) {
2603        return 1;
2604    }
2605
2606    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
2607        admin_user = NULL;
2608    }
2609    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
2610        admin_pass = NULL;
2611    }
2612
2613    if (admin_user == NULL || admin_pass == NULL) {
2614        php_error_docref(XCACHE_WIKI_URL "/InstallAdministration" TSRMLS_CC, E_ERROR,
2615                "xcache.admin.user and/or xcache.admin.pass settings is not configured."
2616                " Make sure you've modified the correct php ini file for your php used in webserver.");
2617        zend_bailout();
2618    }
2619    if (strlen(admin_pass) != 32) {
2620        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.pass is %lu chars unexpectedly, it is supposed to be the password after md5() which should be 32 chars", (unsigned long) strlen(admin_pass));
2621        zend_bailout();
2622    }
2623
2624#ifdef ZEND_ENGINE_2_1
2625    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
2626#endif
2627    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
2628        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
2629        zend_bailout();
2630    }
2631    ht = HASH_OF((*server));
2632
2633    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
2634        user = NULL;
2635    }
2636    else if (Z_TYPE_PP(user) != IS_STRING) {
2637        user = NULL;
2638    }
2639
2640    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
2641        pass = NULL;
2642    }
2643    else if (Z_TYPE_PP(pass) != IS_STRING) {
2644        pass = NULL;
2645    }
2646
2647    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
2648        PHP_MD5_CTX context;
2649        char md5str[33];
2650        unsigned char digest[16];
2651
2652        PHP_MD5Init(&context);
2653        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
2654        PHP_MD5Final(digest, &context);
2655
2656        md5str[0] = '\0';
2657        make_digest(md5str, digest);
2658        if (strcmp(admin_pass, md5str) == 0) {
2659            return 1;
2660        }
2661    }
2662
2663#define STR "HTTP/1.0 401 Unauthorized"
2664    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2665#undef STR
2666#define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
2667    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2668#undef STR
2669#define STR "Content-type: text/html; charset=UTF-8"
2670    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2671#undef STR
2672    ZEND_PUTS("<html>\n");
2673    ZEND_PUTS("<head><title>XCache Authentication Failed</title></head>\n");
2674    ZEND_PUTS("<body>\n");
2675    ZEND_PUTS("<h1>XCache Authentication Failed</h1>\n");
2676    ZEND_PUTS("<p>You're not authorized to access this page due to wrong username and/or password you typed.<br />The following check points is suggested:</p>\n");
2677    ZEND_PUTS("<ul>\n");
2678    ZEND_PUTS("<li>Be aware that `Username' and `Password' is case sense. Check capslock status led on your keyboard, and punch left/right Shift keys once for each</li>\n");
2679    ZEND_PUTS("<li>Make sure the md5 password is generated correctly. You may use <a href=\"mkpassword.php\">mkpassword.php</a></li>\n");
2680    ZEND_PUTS("<li>Reload browser cache by pressing F5 and/or Ctrl+F5, or simply clear browser cache after you've updated username/password in php ini.</li>\n");
2681    ZEND_PUTS("</ul>\n");
2682    ZEND_PUTS("Check <a href=\"" XCACHE_WIKI_URL "/InstallAdministration\">XCache wiki page</a> for more information.\n");
2683    ZEND_PUTS("</body>\n");
2684    ZEND_PUTS("</html>\n");
2685
2686    zend_bailout();
2687    return 0;
2688}
2689/* }}} */
2690/* {{{ xcache_admin_operate */
2691typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
2692static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
2693{
2694    long type;
2695    int size;
2696    xc_cache_t **caches, *cache;
2697    long id = 0;
2698
2699    xcache_admin_auth_check(TSRMLS_C);
2700
2701    if (!xc_initized) {
2702        RETURN_NULL();
2703    }
2704
2705    if (optype == XC_OP_COUNT) {
2706        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2707            return;
2708        }
2709    }
2710    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
2711        return;
2712    }
2713
2714    switch (type) {
2715        case XC_TYPE_PHP:
2716            size = xc_php_hcache.size;
2717            caches = xc_php_caches;
2718            break;
2719
2720        case XC_TYPE_VAR:
2721            size = xc_var_hcache.size;
2722            caches = xc_var_caches;
2723            break;
2724
2725        default:
2726            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
2727            RETURN_FALSE;
2728    }
2729
2730    switch (optype) {
2731        case XC_OP_COUNT:
2732            RETURN_LONG(size)
2733            break;
2734
2735        case XC_OP_INFO:
2736        case XC_OP_LIST:
2737            if (id < 0 || id >= size) {
2738                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2739                RETURN_FALSE;
2740            }
2741
2742            array_init(return_value);
2743
2744            cache = caches[id];
2745            ENTER_LOCK(cache) {
2746                if (optype == XC_OP_INFO) {
2747                    xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
2748                }
2749                else {
2750                    xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
2751                }
2752            } LEAVE_LOCK(cache);
2753            break;
2754        case XC_OP_CLEAR:
2755            {
2756                xc_entry_t *e, *next;
2757                int entryslotid, c;
2758
2759                if (id < 0 || id >= size) {
2760                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2761                    RETURN_FALSE;
2762                }
2763
2764                cache = caches[id];
2765                ENTER_LOCK(cache) {
2766                    for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
2767                        for (e = cache->entries[entryslotid]; e; e = next) {
2768                            next = e->next;
2769                            xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
2770                        }
2771                        cache->entries[entryslotid] = NULL;
2772                    }
2773                } LEAVE_LOCK(cache);
2774                xc_gc_deletes(TSRMLS_C);
2775            }
2776            break;
2777
2778        default:
2779            assert(0);
2780    }
2781}
2782/* }}} */
2783/* {{{ proto int xcache_count(int type)
2784   Return count of cache on specified cache type */
2785PHP_FUNCTION(xcache_count)
2786{
2787    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2788}
2789/* }}} */
2790/* {{{ proto array xcache_info(int type, int id)
2791   Get cache info by id on specified cache type */
2792PHP_FUNCTION(xcache_info)
2793{
2794    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2795}
2796/* }}} */
2797/* {{{ proto array xcache_list(int type, int id)
2798   Get cache entries list by id on specified cache type */
2799PHP_FUNCTION(xcache_list)
2800{
2801    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2802}
2803/* }}} */
2804/* {{{ proto array xcache_clear_cache(int type, int id)
2805   Clear cache by id on specified cache type */
2806PHP_FUNCTION(xcache_clear_cache)
2807{
2808    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2809}
2810/* }}} */
2811
2812#define VAR_DISABLED_WARNING() do { \
2813        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.var_size is either 0 or too small to enable var data caching"); \
2814} while (0)
2815
2816static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
2817{
2818    xc_hash_value_t hv;
2819
2820    switch (name->type) {
2821#ifdef IS_UNICODE
2822        case IS_UNICODE:
2823        case IS_STRING:
2824#endif
2825        default:
2826#ifdef IS_UNICODE
2827            convert_to_unicode(name);
2828#else
2829            convert_to_string(name);
2830#endif
2831    }
2832
2833#ifdef IS_UNICODE
2834    entry_var->name_type = name->type;
2835#endif
2836    entry_var->entry.name = name->value;
2837
2838    hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
2839
2840    entry_hash->cacheid = (hv & xc_var_hcache.mask);
2841    hv >>= xc_var_hcache.bits;
2842    entry_hash->entryslotid = (hv & xc_var_hentry.mask);
2843    return SUCCESS;
2844}
2845/* }}} */
2846/* {{{ proto mixed xcache_get(string name)
2847   Get cached data by specified name */
2848PHP_FUNCTION(xcache_get)
2849{
2850    xc_entry_hash_t entry_hash;
2851    xc_cache_t *cache;
2852    xc_entry_var_t entry_var, *stored_entry_var;
2853    zval *name;
2854
2855    if (!xc_var_caches) {
2856        VAR_DISABLED_WARNING();
2857        RETURN_NULL();
2858    }
2859
2860    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2861        return;
2862    }
2863    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2864    cache = xc_var_caches[entry_hash.cacheid];
2865
2866    ENTER_LOCK(cache) {
2867        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2868        if (stored_entry_var) {
2869            /* return */
2870            xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
2871            xc_cache_hit_unlocked(cache TSRMLS_CC);
2872        }
2873        else {
2874            RETVAL_NULL();
2875        }
2876    } LEAVE_LOCK(cache);
2877}
2878/* }}} */
2879/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
2880   Store data to cache by specified name */
2881PHP_FUNCTION(xcache_set)
2882{
2883    xc_entry_hash_t entry_hash;
2884    xc_cache_t *cache;
2885    xc_entry_var_t entry_var, *stored_entry_var;
2886    zval *name;
2887    zval *value;
2888
2889    if (!xc_var_caches) {
2890        VAR_DISABLED_WARNING();
2891        RETURN_NULL();
2892    }
2893
2894    entry_var.entry.ttl = XG(var_ttl);
2895    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
2896        return;
2897    }
2898
2899    /* max ttl */
2900    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
2901        entry_var.entry.ttl = xc_var_maxttl;
2902    }
2903
2904    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2905    cache = xc_var_caches[entry_hash.cacheid];
2906
2907    ENTER_LOCK(cache) {
2908        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2909        if (stored_entry_var) {
2910            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2911        }
2912        entry_var.value = value;
2913        RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
2914    } LEAVE_LOCK(cache);
2915}
2916/* }}} */
2917/* {{{ proto bool  xcache_isset(string name)
2918   Check if an entry exists in cache by specified name */
2919PHP_FUNCTION(xcache_isset)
2920{
2921    xc_entry_hash_t entry_hash;
2922    xc_cache_t *cache;
2923    xc_entry_var_t entry_var, *stored_entry_var;
2924    zval *name;
2925
2926    if (!xc_var_caches) {
2927        VAR_DISABLED_WARNING();
2928        RETURN_FALSE;
2929    }
2930
2931    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2932        return;
2933    }
2934    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2935    cache = xc_var_caches[entry_hash.cacheid];
2936
2937    ENTER_LOCK(cache) {
2938        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2939        if (stored_entry_var) {
2940            xc_cache_hit_unlocked(cache TSRMLS_CC);
2941            RETVAL_TRUE;
2942            /* return */
2943        }
2944        else {
2945            RETVAL_FALSE;
2946        }
2947
2948    } LEAVE_LOCK(cache);
2949}
2950/* }}} */
2951/* {{{ proto bool  xcache_unset(string name)
2952   Unset existing data in cache by specified name */
2953PHP_FUNCTION(xcache_unset)
2954{
2955    xc_entry_hash_t entry_hash;
2956    xc_cache_t *cache;
2957    xc_entry_var_t entry_var, *stored_entry_var;
2958    zval *name;
2959
2960    if (!xc_var_caches) {
2961        VAR_DISABLED_WARNING();
2962        RETURN_FALSE;
2963    }
2964
2965    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2966        return;
2967    }
2968    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2969    cache = xc_var_caches[entry_hash.cacheid];
2970
2971    ENTER_LOCK(cache) {
2972        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2973        if (stored_entry_var) {
2974            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2975            RETVAL_TRUE;
2976        }
2977        else {
2978            RETVAL_FALSE;
2979        }
2980    } LEAVE_LOCK(cache);
2981}
2982/* }}} */
2983/* {{{ proto bool  xcache_unset_by_prefix(string prefix)
2984   Unset existing data in cache by specified prefix */
2985PHP_FUNCTION(xcache_unset_by_prefix)
2986{
2987    zval *prefix;
2988    int i, iend;
2989
2990    if (!xc_var_caches) {
2991        VAR_DISABLED_WARNING();
2992        RETURN_FALSE;
2993    }
2994
2995    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
2996        return;
2997    }
2998
2999    for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
3000        xc_cache_t *cache = xc_var_caches[i];
3001        ENTER_LOCK(cache) {
3002            int entryslotid, jend;
3003            for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
3004                xc_entry_t *entry, *next;
3005                for (entry = cache->entries[entryslotid]; entry; entry = next) {
3006                    next = entry->next;
3007                    if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
3008                        xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
3009                    }
3010                }
3011            }
3012        } LEAVE_LOCK(cache);
3013    }
3014}
3015/* }}} */
3016static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
3017{
3018    xc_entry_hash_t entry_hash;
3019    xc_cache_t *cache;
3020    xc_entry_var_t entry_var, *stored_entry_var;
3021    zval *name;
3022    long count = 1;
3023    long value = 0;
3024    zval oldzval;
3025
3026    if (!xc_var_caches) {
3027        VAR_DISABLED_WARNING();
3028        RETURN_NULL();
3029    }
3030
3031    entry_var.entry.ttl = XG(var_ttl);
3032    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
3033        return;
3034    }
3035
3036    /* max ttl */
3037    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
3038        entry_var.entry.ttl = xc_var_maxttl;
3039    }
3040
3041    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
3042    cache = xc_var_caches[entry_hash.cacheid];
3043
3044    ENTER_LOCK(cache) {
3045        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
3046        if (stored_entry_var) {
3047            TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
3048            /* do it in place */
3049            if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
3050                zval *zv;
3051                stored_entry_var->entry.ctime = XG(request_time);
3052                stored_entry_var->entry.ttl   = entry_var.entry.ttl;
3053                TRACE("%s", "incdec: islong");
3054                value = Z_LVAL_P(stored_entry_var->value);
3055                value += (inc == 1 ? count : - count);
3056                RETVAL_LONG(value);
3057
3058                zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
3059                Z_LVAL_P(zv) = value;
3060                ++cache->updates;
3061                break; /* leave lock */
3062            }
3063
3064            TRACE("%s", "incdec: notlong");
3065            xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
3066            convert_to_long(&oldzval);
3067            value = Z_LVAL(oldzval);
3068            zval_dtor(&oldzval);
3069        }
3070        else {
3071            TRACE("incdec: %s not found", entry_var.entry.name.str.val);
3072        }
3073
3074        value += (inc == 1 ? count : - count);
3075        RETVAL_LONG(value);
3076        entry_var.value = return_value;
3077
3078        if (stored_entry_var) {
3079            entry_var.entry.atime = stored_entry_var->entry.atime;
3080            entry_var.entry.ctime = stored_entry_var->entry.ctime;
3081            entry_var.entry.hits  = stored_entry_var->entry.hits;
3082            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
3083        }
3084        xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
3085    } LEAVE_LOCK(cache);
3086}
3087/* }}} */
3088/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
3089   Increase an int counter in cache by specified name, create it if not exists */
3090PHP_FUNCTION(xcache_inc)
3091{
3092    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3093}
3094/* }}} */
3095/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
3096   Decrease an int counter in cache by specified name, create it if not exists */
3097PHP_FUNCTION(xcache_dec)
3098{
3099    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3100}
3101/* }}} */
3102/* {{{ proto int xcache_get_refcount(mixed variable)
3103   XCache internal uses only: Get reference count of variable */
3104PHP_FUNCTION(xcache_get_refcount)
3105{
3106    zval *variable;
3107    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3108        RETURN_NULL();
3109    }
3110
3111    RETURN_LONG(Z_REFCOUNT(*variable));
3112}
3113/* }}} */
3114/* {{{ proto bool xcache_get_isref(mixed variable)
3115   XCache internal uses only: Check if variable data is marked referenced */
3116ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
3117    ZEND_ARG_INFO(1, variable)
3118ZEND_END_ARG_INFO()
3119
3120PHP_FUNCTION(xcache_get_isref)
3121{
3122    zval *variable;
3123    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3124        RETURN_NULL();
3125    }
3126
3127    RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
3128}
3129/* }}} */
3130#ifdef HAVE_XCACHE_DPRINT
3131/* {{{ proto bool  xcache_dprint(mixed value)
3132   Prints variable (or value) internal struct (debug only) */
3133PHP_FUNCTION(xcache_dprint)
3134{
3135    zval *value;
3136
3137    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3138        return;
3139    }
3140    xc_dprint_zval(value, 0 TSRMLS_CC);
3141}
3142/* }}} */
3143#endif
3144/* {{{ proto string xcache_asm(string filename)
3145 */
3146#ifdef HAVE_XCACHE_ASSEMBLER
3147PHP_FUNCTION(xcache_asm)
3148{
3149}
3150#endif
3151/* }}} */
3152#ifdef HAVE_XCACHE_DISASSEMBLER
3153/* {{{ proto array  xcache_dasm_file(string filename)
3154   Disassemble file into opcode array by filename */
3155PHP_FUNCTION(xcache_dasm_file)
3156{
3157    char *filename;
3158    int filename_len;
3159
3160    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
3161        return;
3162    }
3163    if (!filename_len) RETURN_FALSE;
3164
3165    xc_dasm_file(return_value, filename TSRMLS_CC);
3166}
3167/* }}} */
3168/* {{{ proto array  xcache_dasm_string(string code)
3169   Disassemble php code into opcode array */
3170PHP_FUNCTION(xcache_dasm_string)
3171{
3172    zval *code;
3173
3174    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
3175        return;
3176    }
3177    xc_dasm_string(return_value, code TSRMLS_CC);
3178}
3179/* }}} */
3180#endif
3181/* {{{ proto string xcache_encode(string filename)
3182   Encode php file into XCache opcode encoded format */
3183#ifdef HAVE_XCACHE_ENCODER
3184PHP_FUNCTION(xcache_encode)
3185{
3186}
3187#endif
3188/* }}} */
3189/* {{{ proto bool xcache_decode_file(string filename)
3190   Decode(load) opcode from XCache encoded format file */
3191#ifdef HAVE_XCACHE_DECODER
3192PHP_FUNCTION(xcache_decode_file)
3193{
3194}
3195#endif
3196/* }}} */
3197/* {{{ proto bool xcache_decode_string(string data)
3198   Decode(load) opcode from XCache encoded format data */
3199#ifdef HAVE_XCACHE_DECODER
3200PHP_FUNCTION(xcache_decode_string)
3201{
3202}
3203#endif
3204/* }}} */
3205/* {{{ xc_call_getter */
3206typedef const char *(xc_name_getter_t)(zend_uchar type);
3207static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
3208{
3209    long spec;
3210    const char *name;
3211
3212    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3213        return;
3214    }
3215    if (spec >= 0 && spec < count) {
3216        name = getter((zend_uchar) spec);
3217        if (name) {
3218            /* RETURN_STRING */
3219            int len = strlen(name);
3220            return_value->value.str.len = len;
3221            return_value->value.str.val = estrndup(name, len);
3222            return_value->type = IS_STRING; 
3223            return;
3224        }
3225    }
3226    RETURN_NULL();
3227}
3228/* }}} */
3229/* {{{ proto string xcache_get_op_type(int op_type) */
3230PHP_FUNCTION(xcache_get_op_type)
3231{
3232    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3233}
3234/* }}} */
3235/* {{{ proto string xcache_get_data_type(int type) */
3236PHP_FUNCTION(xcache_get_data_type)
3237{
3238    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3239}
3240/* }}} */
3241/* {{{ proto string xcache_get_opcode(int opcode) */
3242PHP_FUNCTION(xcache_get_opcode)
3243{
3244    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3245}
3246/* }}} */
3247/* {{{ proto string xcache_get_op_spec(int op_type) */
3248PHP_FUNCTION(xcache_get_op_spec)
3249{
3250    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3251}
3252/* }}} */
3253#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
3254/* {{{ proto string xcache_get_opcode_spec(int opcode) */
3255PHP_FUNCTION(xcache_get_opcode_spec)
3256{
3257    long spec;
3258    const xc_opcode_spec_t *opspec;
3259
3260    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3261        return;
3262    }
3263    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
3264        opspec = xc_get_opcode_spec((zend_uchar) spec);
3265        if (opspec) {
3266            array_init(return_value);
3267            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
3268            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
3269            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
3270            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
3271            return;
3272        }
3273    }
3274    RETURN_NULL();
3275}
3276/* }}} */
3277#endif
3278/* {{{ proto mixed xcache_get_special_value(zval value)
3279   XCache internal use only: For decompiler to get static value with type fixed */
3280PHP_FUNCTION(xcache_get_special_value)
3281{
3282    zval *value;
3283
3284    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3285        return;
3286    }
3287
3288    switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
3289    case IS_CONSTANT:
3290        *return_value = *value;
3291        zval_copy_ctor(return_value);
3292        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
3293        break;
3294
3295    case IS_CONSTANT_ARRAY:
3296        *return_value = *value;
3297        zval_copy_ctor(return_value);
3298        return_value->type = IS_ARRAY;
3299        break;
3300
3301    default:
3302        RETURN_NULL();
3303    }
3304}
3305/* }}} */
3306/* {{{ proto int xcache_get_type(zval value)
3307   XCache internal use only for disassembler to get variable type in engine level */
3308PHP_FUNCTION(xcache_get_type)
3309{
3310    zval *value;
3311
3312    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3313        return;
3314    }
3315
3316    RETURN_LONG(Z_TYPE_P(value));
3317}
3318/* }}} */
3319/* {{{ proto string xcache_coredump(int op_type) */
3320PHP_FUNCTION(xcache_coredump)
3321{
3322    if (xc_test) {
3323        raise(SIGSEGV);
3324    }
3325    else {
3326        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
3327    }
3328}
3329/* }}} */
3330/* {{{ proto string xcache_is_autoglobal(string name) */
3331PHP_FUNCTION(xcache_is_autoglobal)
3332{
3333    zval *name;
3334
3335    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
3336        return;
3337    }
3338
3339#ifdef IS_UNICODE
3340    convert_to_unicode(name);
3341#else
3342    convert_to_string(name);
3343#endif
3344
3345    RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
3346}
3347/* }}} */
3348static zend_function_entry xcache_functions[] = /* {{{ */
3349{
3350    PHP_FE(xcache_count,             NULL)
3351    PHP_FE(xcache_info,              NULL)
3352    PHP_FE(xcache_list,              NULL)
3353    PHP_FE(xcache_clear_cache,       NULL)
3354    PHP_FE(xcache_coredump,          NULL)
3355#ifdef HAVE_XCACHE_ASSEMBLER
3356    PHP_FE(xcache_asm,               NULL)
3357#endif
3358#ifdef HAVE_XCACHE_DISASSEMBLER
3359    PHP_FE(xcache_dasm_file,         NULL)
3360    PHP_FE(xcache_dasm_string,       NULL)
3361#endif
3362#ifdef HAVE_XCACHE_ENCODER
3363    PHP_FE(xcache_encode,            NULL)
3364#endif
3365#ifdef HAVE_XCACHE_DECODER
3366    PHP_FE(xcache_decode_file,       NULL)
3367    PHP_FE(xcache_decode_string,     NULL)
3368#endif
3369#ifdef HAVE_XCACHE_COVERAGER
3370    PHP_FE(xcache_coverager_decode,  NULL)
3371    PHP_FE(xcache_coverager_start,   NULL)
3372    PHP_FE(xcache_coverager_stop,    NULL)
3373    PHP_FE(xcache_coverager_get,     NULL)
3374#endif
3375    PHP_FE(xcache_get_special_value, NULL)
3376    PHP_FE(xcache_get_type,          NULL)
3377    PHP_FE(xcache_get_op_type,       NULL)
3378    PHP_FE(xcache_get_data_type,     NULL)
3379    PHP_FE(xcache_get_opcode,        NULL)
3380#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
3381    PHP_FE(xcache_get_opcode_spec,   NULL)
3382#endif
3383    PHP_FE(xcache_is_autoglobal,     NULL)
3384    PHP_FE(xcache_inc,               NULL)
3385    PHP_FE(xcache_dec,               NULL)
3386    PHP_FE(xcache_get,               NULL)
3387    PHP_FE(xcache_set,               NULL)
3388    PHP_FE(xcache_isset,             NULL)
3389    PHP_FE(xcache_unset,             NULL)
3390    PHP_FE(xcache_unset_by_prefix,   NULL)
3391    PHP_FE(xcache_get_refcount,      NULL)
3392    PHP_FE(xcache_get_isref,         arginfo_xcache_get_isref)
3393#ifdef HAVE_XCACHE_DPRINT
3394    PHP_FE(xcache_dprint,            NULL)
3395#endif
3396    {NULL, NULL,                     NULL}
3397};
3398/* }}} */
3399
3400#ifdef ZEND_WIN32
3401#include "dbghelp.h"
3402typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
3403        CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
3404        CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
3405        CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
3406        );
3407
3408static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
3409static HMODULE dbghelpModule = NULL;
3410static char crash_dumpPath[_MAX_PATH] = { 0 };
3411static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
3412
3413static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
3414{
3415    HANDLE fileHandle;
3416
3417    SetUnhandledExceptionFilter(oldFilter);
3418
3419    /* create the file */
3420    fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3421
3422    if (fileHandle != INVALID_HANDLE_VALUE) {
3423        MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
3424        BOOL ok;
3425
3426        exceptionInformation.ThreadId = GetCurrentThreadId();
3427        exceptionInformation.ExceptionPointers = pExceptionInfo;
3428        exceptionInformation.ClientPointers = FALSE;
3429
3430        /* write the dump */
3431        ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory, &exceptionInformation, NULL, NULL);
3432        CloseHandle(fileHandle);
3433        if (ok) {
3434            zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
3435            return EXCEPTION_EXECUTE_HANDLER;
3436        }
3437        else {
3438            zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
3439        }
3440    }
3441    else {
3442        zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
3443    }
3444
3445    return EXCEPTION_CONTINUE_SEARCH;
3446}
3447/* }}} */
3448
3449static void xcache_restore_crash_handler() /* {{{ */
3450{
3451    if (oldFilter) {
3452        SetUnhandledExceptionFilter(oldFilter);
3453        oldFilter = NULL;
3454    }
3455}
3456/* }}} */
3457static void xcache_init_crash_handler() /* {{{ */
3458{
3459    /* firstly see if dbghelp.dll is around and has the function we need
3460       look next to the EXE first, as the one in System32 might be old
3461       (e.g. Windows 2000) */
3462    char dbghelpPath[_MAX_PATH];
3463
3464    if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
3465        char *slash = strchr(dbghelpPath, '\\');
3466        if (slash) {
3467            strcpy(slash + 1, "DBGHELP.DLL");
3468            dbghelpModule = LoadLibrary(dbghelpPath);
3469        }
3470    }
3471
3472    if (!dbghelpModule) {
3473        /* load any version we can */
3474        dbghelpModule = LoadLibrary("DBGHELP.DLL");
3475        if (!dbghelpModule) {
3476            zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
3477            return;
3478        }
3479    }
3480
3481    dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
3482    if (!dbghelp_MiniDumpWriteDump) {
3483        zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL too old. Get updated dll and put it aside of php_xcache.dll");
3484        return;
3485    }
3486
3487#ifdef XCACHE_VERSION_REVISION
3488#define REVISION "r" XCACHE_VERSION_REVISION
3489#else
3490#define REVISION ""
3491#endif
3492    sprintf(crash_dumpPath, "%s\\php-%s-xcache-%s%s-%lu-%lu.dmp", xc_coredump_dir, zend_get_module_version("standard"), XCACHE_VERSION, REVISION, (unsigned long) time(NULL), (unsigned long) GetCurrentProcessId());
3493#undef REVISION
3494
3495    oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
3496}
3497/* }}} */
3498#else
3499/* old signal handlers {{{ */
3500typedef void (*xc_sighandler_t)(int);
3501#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
3502#include "foreachcoresig.h"
3503#undef FOREACH_SIG
3504/* }}} */
3505static void xcache_signal_handler(int sig);
3506static void xcache_restore_crash_handler() /* {{{ */
3507{
3508#define FOREACH_SIG(sig) do { \
3509    if (old_##sig##_handler != xcache_signal_handler) { \
3510        signal(sig, old_##sig##_handler); \
3511    } \
3512    else { \
3513        signal(sig, SIG_DFL); \
3514    } \
3515} while (0)
3516#include "foreachcoresig.h"
3517#undef FOREACH_SIG
3518}
3519/* }}} */
3520static void xcache_init_crash_handler() /* {{{ */
3521{
3522#define FOREACH_SIG(sig) \
3523    old_##sig##_handler = signal(sig, xcache_signal_handler)
3524#include "foreachcoresig.h"
3525#undef FOREACH_SIG
3526}
3527/* }}} */
3528static void xcache_signal_handler(int sig) /* {{{ */
3529{
3530    xcache_restore_crash_handler();
3531    if (xc_coredump_dir && xc_coredump_dir[0]) {
3532        if (chdir(xc_coredump_dir) != 0) {
3533            /* error, but nothing can do about it
3534             * and should'nt print anything which might SEGV again */
3535        }
3536    }
3537    raise(sig);
3538}
3539/* }}} */
3540#endif
3541
3542/* {{{ PHP_INI */
3543
3544static PHP_INI_MH(xc_OnUpdateDummy)
3545{
3546    return SUCCESS;
3547}
3548
3549static PHP_INI_MH(xc_OnUpdateULong)
3550{
3551    zend_ulong *p = (zend_ulong *) mh_arg1;
3552
3553    *p = (zend_ulong) atoi(new_value);
3554    return SUCCESS;
3555}
3556
3557static PHP_INI_MH(xc_OnUpdateBool)
3558{
3559    zend_bool *p = (zend_bool *)mh_arg1;
3560
3561    if (strncasecmp("on", new_value, sizeof("on"))) {
3562        *p = (zend_bool) atoi(new_value);
3563    }
3564    else {
3565        *p = (zend_bool) 1;
3566    }
3567    return SUCCESS;
3568}
3569
3570static PHP_INI_MH(xc_OnUpdateString)
3571{
3572    char **p = (char**)mh_arg1;
3573    if (*p) {
3574        pefree(*p, 1);
3575    }
3576    *p = pemalloc(strlen(new_value) + 1, 1);
3577    strcpy(*p, new_value);
3578    return SUCCESS;
3579}
3580
3581#ifndef ZEND_ENGINE_2
3582#define OnUpdateLong OnUpdateInt
3583#endif
3584
3585#ifdef ZEND_WIN32
3586#   define DEFAULT_PATH "xcache"
3587#else
3588#   define DEFAULT_PATH "/dev/zero"
3589#endif
3590PHP_INI_BEGIN()
3591    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
3592    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
3593    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
3594    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
3595    /* opcode cache */
3596    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3597    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3598    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3599    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
3600    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
3601    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
3602    /* var cache */
3603    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3604    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3605    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3606    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
3607    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
3608
3609    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
3610    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
3611    STD_PHP_INI_BOOLEAN("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, OnUpdateBool,        auth_enabled,      zend_xcache_globals, xcache_globals)
3612    STD_PHP_INI_BOOLEAN("xcache.experimental",           "0", PHP_INI_ALL,    OnUpdateBool,        experimental,      zend_xcache_globals, xcache_globals)
3613#ifdef HAVE_XCACHE_OPTIMIZER
3614    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
3615#endif
3616    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
3617#ifdef HAVE_XCACHE_COVERAGER
3618    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
3619    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3620#endif
3621PHP_INI_END()
3622/* }}} */
3623/* {{{ PHP_MINFO_FUNCTION(xcache) */
3624static PHP_MINFO_FUNCTION(xcache)
3625{
3626    char buf[100];
3627    char *ptr;
3628    int left, len;
3629    xc_shm_scheme_t *scheme;
3630#ifdef HAVE_XCACHE_COVERAGER
3631    char *covdumpdir;
3632#endif
3633
3634    php_info_print_table_start();
3635    php_info_print_table_header(2, "XCache Support", "enabled");
3636    php_info_print_table_row(2, "Version", XCACHE_VERSION);
3637#ifdef XCACHE_VERSION_REVISION
3638    php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
3639#endif
3640    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
3641    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
3642#ifdef ZEND_ENGINE_2_1
3643    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
3644    php_info_print_table_row(2, "Cache Init Time", ptr);
3645    efree(ptr);
3646#else
3647    snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
3648    php_info_print_table_row(2, "Cache Init Time", buf);
3649#endif
3650
3651#ifdef ZTS
3652    snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
3653#else
3654    snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
3655#endif
3656    php_info_print_table_row(2, "Cache Instance Id", buf);
3657
3658    if (xc_php_size) {
3659        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
3660        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
3661        php_info_print_table_row(2, "Opcode Cache", buf);
3662        efree(ptr);
3663    }
3664    else {
3665        php_info_print_table_row(2, "Opcode Cache", "disabled");
3666    }
3667    if (xc_var_size) {
3668        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
3669        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
3670        php_info_print_table_row(2, "Variable Cache", buf);
3671        efree(ptr);
3672    }
3673    else {
3674        php_info_print_table_row(2, "Variable Cache", "disabled");
3675    }
3676
3677    left = sizeof(buf);
3678    ptr = buf;
3679    buf[0] = '\0';
3680    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
3681        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
3682        left -= len;
3683        ptr += len;
3684    }
3685    php_info_print_table_row(2, "Shared Memory Schemes", buf);
3686
3687#ifdef HAVE_XCACHE_COVERAGER
3688    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
3689        covdumpdir = NULL;
3690    }
3691    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
3692#endif
3693    php_info_print_table_end();
3694
3695    DISPLAY_INI_ENTRIES();
3696}
3697/* }}} */
3698/* {{{ extension startup */
3699static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
3700{
3701    zend_extension extension;
3702
3703    extension = *new_extension;
3704    extension.handle = handle;
3705
3706    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
3707
3708    zend_llist_prepend_element(&zend_extensions, &extension);
3709    TRACE("%s", "registered");
3710}
3711
3712static zend_llist_element *xc_llist_get_element_by_zend_extension(zend_llist *l, const char *extension_name)
3713{
3714    zend_llist_element *element;
3715
3716    for (element = zend_extensions.head; element; element = element->next) {
3717        zend_extension *extension = (zend_extension *) element->data;
3718
3719        if (!strcmp(extension->name, extension_name)) {
3720            return element;
3721        }
3722    }
3723    return NULL;
3724}
3725
3726static void xc_llist_prepend(zend_llist *l, zend_llist_element *element)
3727{
3728    element->next = l->head;
3729    element->prev = NULL;
3730    if (l->head) {
3731        l->head->prev = element;
3732    }
3733    else {
3734        l->tail = element;
3735    }
3736    l->head = element;
3737    ++l->count;
3738}
3739
3740static void xc_llist_unlink(zend_llist *l, zend_llist_element *element)
3741{
3742    if ((element)->prev) {
3743        (element)->prev->next = (element)->next;
3744    }
3745    else {
3746        (l)->head = (element)->next;
3747    }
3748
3749    if ((element)->next) {
3750        (element)->next->prev = (element)->prev;
3751    }
3752    else {
3753        (l)->tail = (element)->prev;
3754    }
3755
3756    --l->count;
3757}
3758
3759static int xc_zend_extension_startup(zend_extension *extension)
3760{
3761    if (extension->startup) {
3762        if (extension->startup(extension) != SUCCESS) {
3763            return FAILURE;
3764        }
3765    }
3766    return SUCCESS;
3767}
3768/* }}} */
3769static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
3770{
3771    return p1 == p2;
3772}
3773/* }}} */
3774static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
3775{
3776    llist_dtor_func_t dtor;
3777
3778    assert(extension);
3779    dtor = zend_extensions.dtor; /* avoid dtor */
3780    zend_extensions.dtor = NULL;
3781    zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
3782    zend_extensions.dtor = dtor;
3783    return SUCCESS;
3784}
3785/* }}} */
3786static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
3787{
3788    size_t bits, size;
3789    char *value;
3790
3791    if (cfg_get_string(name, &value) != SUCCESS) {
3792        value = default_value;
3793    }
3794
3795    p->size = zend_atoi(value, strlen(value));
3796    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
3797        /* empty body */
3798    }
3799    p->size = size;
3800    p->bits = bits;
3801    p->mask = size - 1;
3802
3803    return SUCCESS;
3804}
3805/* }}} */
3806static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
3807{
3808    char *value;
3809
3810    if (cfg_get_string(name, &value) != SUCCESS) {
3811        value = default_value;
3812    }
3813
3814    *p = zend_atol(value, strlen(value));
3815    return SUCCESS;
3816}
3817/* }}} */
3818/* {{{ PHP_MINIT_FUNCTION(xcache) */
3819static PHP_MINIT_FUNCTION(xcache)
3820{
3821    char *env;
3822    zend_extension *ext;
3823    zend_llist_position lpos;
3824
3825    xc_module_gotup = 1;
3826    if (!xc_zend_extension_gotup) {
3827        xc_zend_extension_register(&zend_extension_entry, 0);
3828        xc_zend_extension_startup(&zend_extension_entry);
3829        xc_zend_extension_faked = 1;
3830    }
3831
3832    ext = zend_get_extension("Zend Optimizer");
3833    if (ext) {
3834        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
3835        ext->op_array_handler = NULL;
3836    }
3837    /* cache if there's an op_array_ctor */
3838    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
3839            ext;
3840            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
3841        if (ext->op_array_ctor) {
3842            xc_have_op_array_ctor = 1;
3843            break;
3844        }
3845    }
3846
3847
3848#ifndef PHP_GINIT
3849    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
3850#endif
3851    REGISTER_INI_ENTRIES();
3852
3853    xc_config_long(&xc_php_size,       "xcache.size",        "0");
3854    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
3855    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
3856
3857    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
3858    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
3859    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
3860
3861    if (strcmp(sapi_module.name, "cli") == 0) {
3862        if ((env = getenv("XCACHE_TEST")) != NULL) {
3863            xc_test = atoi(env);
3864        }
3865        if (!xc_test) {
3866            /* disable cache for cli except for testing */
3867            xc_php_size = xc_var_size = 0;
3868        }
3869    }
3870
3871    if (xc_php_size <= 0) {
3872        xc_php_size = xc_php_hcache.size = 0;
3873    }
3874    if (xc_var_size <= 0) {
3875        xc_var_size = xc_var_hcache.size = 0;
3876    }
3877
3878    if (xc_coredump_dir && xc_coredump_dir[0]) {
3879        xcache_init_crash_handler();
3880    }
3881
3882    xc_init_constant(module_number TSRMLS_CC);
3883    xc_shm_init_modules();
3884
3885    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
3886        if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
3887            zend_error(E_ERROR, "XCache: Cannot init");
3888            goto err_init;
3889        }
3890        xc_initized = 1;
3891        xc_init_time = time(NULL);
3892#ifdef PHP_WIN32
3893        xc_init_instance_id = GetCurrentProcessId();
3894#else
3895        xc_init_instance_id = getpid();
3896#endif
3897#ifdef ZTS
3898        xc_init_instance_subid = tsrm_thread_id();
3899#endif
3900    }
3901
3902    xc_util_init(module_number TSRMLS_CC);
3903#ifdef HAVE_XCACHE_COVERAGER
3904    xc_coverager_init(module_number TSRMLS_CC);
3905#endif
3906
3907    return SUCCESS;
3908
3909err_init:
3910    return FAILURE;
3911}
3912/* }}} */
3913/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
3914static PHP_MSHUTDOWN_FUNCTION(xcache)
3915{
3916#ifdef HAVE_XCACHE_COVERAGER
3917    xc_coverager_destroy();
3918#endif
3919    xc_util_destroy();
3920
3921    if (xc_initized) {
3922        xc_destroy();
3923    }
3924    if (xc_mmap_path) {
3925        pefree(xc_mmap_path, 1);
3926        xc_mmap_path = NULL;
3927    }
3928    if (xc_shm_scheme) {
3929        pefree(xc_shm_scheme, 1);
3930        xc_shm_scheme = NULL;
3931    }
3932
3933    if (xc_coredump_dir && xc_coredump_dir[0]) {
3934        xcache_restore_crash_handler();
3935    }
3936    if (xc_coredump_dir) {
3937        pefree(xc_coredump_dir, 1);
3938        xc_coredump_dir = NULL;
3939    }
3940#ifndef PHP_GINIT
3941#   ifdef ZTS
3942    ts_free_id(xcache_globals_id);
3943#   else
3944    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
3945#   endif
3946#endif
3947
3948    if (xc_zend_extension_faked) {
3949        zend_extension *ext = zend_get_extension(XCACHE_NAME);
3950        if (ext) {
3951            if (ext->shutdown) {
3952                ext->shutdown(ext);
3953            }
3954            xc_zend_remove_extension(ext);
3955        }
3956    }
3957    UNREGISTER_INI_ENTRIES();
3958
3959    xc_module_gotup = 0;
3960    xc_zend_extension_gotup = 0;
3961    xc_zend_extension_faked = 0;
3962
3963    return SUCCESS;
3964}
3965/* }}} */
3966/* {{{ PHP_RINIT_FUNCTION(xcache) */
3967static PHP_RINIT_FUNCTION(xcache)
3968{
3969    xc_request_init(TSRMLS_C);
3970    return SUCCESS;
3971}
3972/* }}} */
3973/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
3974#ifndef ZEND_ENGINE_2
3975static PHP_RSHUTDOWN_FUNCTION(xcache)
3976#else
3977static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
3978#endif
3979{
3980#ifdef ZEND_ENGINE_2
3981    TSRMLS_FETCH();
3982#endif
3983
3984    xc_request_shutdown(TSRMLS_C);
3985    return SUCCESS;
3986}
3987/* }}} */
3988/* {{{ module dependencies */
3989#if ZEND_MODULE_API_NO >= 20050922
3990static zend_module_dep xcache_module_deps[] = {
3991    ZEND_MOD_REQUIRED("standard")
3992    ZEND_MOD_CONFLICTS("apc")
3993    ZEND_MOD_CONFLICTS("eAccelerator")
3994    ZEND_MOD_CONFLICTS("Turck MMCache")
3995    {NULL, NULL, NULL}
3996};
3997#endif
3998/* }}} */ 
3999/* {{{ module definition structure */
4000
4001zend_module_entry xcache_module_entry = {
4002#if ZEND_MODULE_API_NO >= 20050922
4003    STANDARD_MODULE_HEADER_EX,
4004    NULL,
4005    xcache_module_deps,
4006#else
4007    STANDARD_MODULE_HEADER,
4008#endif
4009    XCACHE_NAME,
4010    xcache_functions,
4011    PHP_MINIT(xcache),
4012    PHP_MSHUTDOWN(xcache),
4013    PHP_RINIT(xcache),
4014#ifndef ZEND_ENGINE_2
4015    PHP_RSHUTDOWN(xcache),
4016#else
4017    NULL,
4018#endif
4019    PHP_MINFO(xcache),
4020    XCACHE_VERSION,
4021#ifdef PHP_GINIT
4022    PHP_MODULE_GLOBALS(xcache),
4023    PHP_GINIT(xcache),
4024    PHP_GSHUTDOWN(xcache),
4025#endif
4026#ifdef ZEND_ENGINE_2
4027    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
4028#else
4029    NULL,
4030    NULL,
4031#endif
4032    STANDARD_MODULE_PROPERTIES_EX
4033};
4034
4035#ifdef COMPILE_DL_XCACHE
4036ZEND_GET_MODULE(xcache)
4037#endif
4038/* }}} */
4039static startup_func_t xc_last_ext_startup;
4040static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
4041{
4042    /* restore */
4043    extension->startup = xc_last_ext_startup;
4044    if (extension->startup) {
4045        if (extension->startup(extension) != SUCCESS) {
4046            return FAILURE;
4047        }
4048    }
4049    assert(xc_llist_zend_extension);
4050    xc_llist_prepend(&zend_extensions, xc_llist_zend_extension);
4051    if (!xc_module_gotup) {
4052        return zend_startup_module(&xcache_module_entry);
4053    }
4054    return SUCCESS;
4055}
4056/* }}} */
4057ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
4058{
4059    xc_zend_extension_gotup = 1;
4060
4061    if (!origin_compile_file) {
4062        origin_compile_file = zend_compile_file;
4063        zend_compile_file = xc_check_initial_compile_file;
4064    }
4065
4066    if (zend_llist_count(&zend_extensions) > 1) {
4067        zend_llist_position lpos;
4068        zend_extension *ext;
4069
4070        xc_llist_zend_extension = xc_llist_get_element_by_zend_extension(&zend_extensions, XCACHE_NAME);
4071        xc_llist_unlink(&zend_extensions, xc_llist_zend_extension);
4072
4073        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
4074        assert(ext && ext != (zend_extension *) xc_llist_zend_extension->data);
4075        xc_last_ext_startup = ext->startup;
4076        ext->startup = xc_zend_startup_last;
4077    }
4078    else if (!xc_module_gotup) {
4079        return zend_startup_module(&xcache_module_entry);
4080    }
4081    return SUCCESS;
4082}
4083/* }}} */
4084ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
4085{
4086    /* empty */
4087}
4088/* }}} */
4089ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
4090{
4091#ifdef HAVE_XCACHE_COVERAGER
4092    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
4093#endif
4094}
4095/* }}} */
4096ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
4097{
4098#if 0
4099    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
4100#endif
4101}
4102/* }}} */
4103ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
4104{
4105#if 0
4106    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
4107#endif
4108}
4109/* }}} */
4110/* {{{ zend extension definition structure */
4111ZEND_DLEXPORT zend_extension zend_extension_entry = {
4112    XCACHE_NAME,
4113    XCACHE_VERSION,
4114    XCACHE_AUTHOR,
4115    XCACHE_URL,
4116    XCACHE_COPYRIGHT,
4117    xcache_zend_startup,
4118    xcache_zend_shutdown,
4119    NULL,           /* activate_func_t */
4120    NULL,           /* deactivate_func_t */
4121    NULL,           /* message_handler_func_t */
4122#ifdef HAVE_XCACHE_OPTIMIZER
4123    xc_optimizer_op_array_handler,
4124#else
4125    NULL,           /* op_array_handler_func_t */
4126#endif
4127    xcache_statement_handler,
4128    xcache_fcall_begin_handler,
4129    xcache_fcall_end_handler,
4130    NULL,           /* op_array_ctor_func_t */
4131    NULL,           /* op_array_dtor_func_t */
4132    STANDARD_ZEND_EXTENSION_PROPERTIES
4133};
4134
4135#ifndef ZEND_EXT_API
4136#   define ZEND_EXT_API ZEND_DLEXPORT
4137#endif
4138#if COMPILE_DL_XCACHE
4139ZEND_EXTENSION();
4140#endif
4141/* }}} */
Note: See TracBrowser for help on using the repository browser.