source: trunk/xcache.c @ 937

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

still WIP: rewrite sandbox to use callback

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