source: trunk/xcache.c @ 929

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

xc_resolve_path for better name. call util/coverager destroy first

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