source: trunk/xcache.c @ 979

Last change on this file since 979 was 979, checked in by moo, 21 months ago

closes #2: auto disable on crash

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