source: trunk/xcache.c @ 950

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

stat on resolved path

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