source: trunk/xcache.c @ 931

Last change on this file since 931 was 931, checked in by moo, 2 years ago

refcount is no longer available for var cache

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