source: trunk/xcache.c @ 235

Last change on this file since 235 was 235, checked in by moo, 8 years ago

cacher: trick in_compilation state, don't trigger __autoload at compile time

File size: 66.6 KB
Line 
1
2#if 0
3#define DEBUG
4#define SHOW_DPRINT
5#endif
6
7/* {{{ macros */
8#include <stdlib.h>
9#include <stdio.h>
10#include <string.h>
11
12#include <signal.h>
13
14#include "php.h"
15#include "ext/standard/info.h"
16#include "ext/standard/md5.h"
17#include "ext/standard/php_math.h"
18#include "zend_extensions.h"
19#include "SAPI.h"
20
21#include "xcache.h"
22#include "optimizer.h"
23#include "coverager.h"
24#include "disassembler.h"
25#include "align.h"
26#include "stack.h"
27#include "xcache_globals.h"
28#include "processor.h"
29#include "utils.h"
30#include "const_string.h"
31#include "opcode_spec.h"
32
33#ifdef DEBUG
34#   undef NDEBUG
35#   undef inline
36#   define inline
37#else
38#   ifndef NDEBUG
39#       define NDEBUG
40#   endif
41#endif
42#include <assert.h>
43
44#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > pentry->ctime + (pentry)->ttl)
45#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
46#define LOCK(x) xc_lock(x->lck)
47#define UNLOCK(x) xc_unlock(x->lck)
48
49#define ENTER_LOCK_EX(x) \
50    xc_lock(x->lck); \
51    zend_try { \
52        do
53#define LEAVE_LOCK_EX(x) \
54        while (0); \
55    } zend_catch { \
56        catched = 1; \
57    } zend_end_try(); \
58    xc_unlock(x->lck)
59
60#define ENTER_LOCK(x) do { \
61    int catched = 0; \
62    ENTER_LOCK_EX(x)
63#define LEAVE_LOCK(x) \
64    LEAVE_LOCK_EX(x); \
65    if (catched) { \
66        zend_bailout(); \
67    } \
68} while(0)
69
70/* }}} */
71
72/* {{{ globals */
73static char *xc_shm_scheme = NULL;
74static char *xc_mmap_path = NULL;
75static char *xc_coredump_dir = NULL;
76
77static xc_hash_t xc_php_hcache = {0};
78static xc_hash_t xc_php_hentry = {0};
79static xc_hash_t xc_var_hcache = {0};
80static xc_hash_t xc_var_hentry = {0};
81
82static zend_ulong xc_php_ttl    = 0;
83static zend_ulong xc_var_maxttl = 0;
84
85enum { xc_deletes_gc_interval = 120 };
86static zend_ulong xc_php_gc_interval = 0;
87static zend_ulong xc_var_gc_interval = 0;
88
89/* total size */
90static zend_ulong xc_php_size  = 0;
91static zend_ulong xc_var_size  = 0;
92
93static xc_cache_t **xc_php_caches = NULL;
94static xc_cache_t **xc_var_caches = NULL;
95
96static zend_bool xc_initized = 0;
97static zend_compile_file_t *origin_compile_file;
98
99static zend_bool xc_test = 0;
100static zend_bool xc_readonly_protection = 0;
101
102zend_bool xc_have_op_array_ctor = 0;
103
104static zend_bool xc_module_gotup = 0;
105static zend_bool xc_zend_extension_gotup = 0;
106#if !COMPILE_DL_XCACHE
107#   define zend_extension_entry xcache_zend_extension_entry
108#endif
109ZEND_DLEXPORT zend_extension zend_extension_entry;
110ZEND_DECLARE_MODULE_GLOBALS(xcache);
111/* }}} */
112
113/* any function in *_dmz is only safe be called within locked(single thread) area */
114
115static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
116{
117    /* this function isn't required but can be in dmz */
118
119    if (a->type != b->type) {
120        return 0;
121    }
122    switch (a->type) {
123        case XC_TYPE_PHP:
124#ifdef HAVE_INODE
125            do {
126                xc_entry_data_php_t *ap = a->data.php;
127                xc_entry_data_php_t *bp = b->data.php;
128                if (ap->inode) {
129                    return ap->inode == bp->inode
130                        && ap->device == bp->device;
131                }
132            } while(0);
133#endif
134            /* fall */
135
136        case XC_TYPE_VAR:
137            do {
138#ifdef IS_UNICODE
139                if (a->name_type == IS_UNICODE) {
140                    if (a->name.ustr.len != b->name.ustr.len) {
141                        return 0;
142                    }
143                    return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
144                }
145                else {
146                    return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
147                }
148#else
149                return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
150#endif
151
152            } while(0);
153        default:
154            assert(0);
155    }
156    return 0;
157}
158/* }}} */
159static void xc_entry_free_real_dmz(volatile xc_entry_t *xce) /* {{{ */
160{
161    xce->cache->mem->handlers->free(xce->cache->mem, (xc_entry_t *)xce);
162}
163/* }}} */
164static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
165{
166    xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
167    xce->next = *head;
168    *head = xce;
169    xce->cache->entries_count ++;
170}
171/* }}} */
172static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
173{
174    xc_entry_t *stored_xce;
175
176    xce->hits  = 0;
177    xce->ctime = XG(request_time);
178    xce->atime = XG(request_time);
179    stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
180    if (stored_xce) {
181        xc_entry_add_dmz(stored_xce);
182        return stored_xce;
183    }
184    else {
185        xce->cache->ooms ++;
186        return NULL;
187    }
188}
189/* }}} */
190static void xc_entry_free_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
191{
192    xce->cache->entries_count --;
193    if (xce->refcount == 0) {
194        xc_entry_free_real_dmz(xce);
195    }
196    else {
197        xce->next = xce->cache->deletes;
198        xce->cache->deletes = xce;
199        xce->dtime = XG(request_time);
200        xce->cache->deletes_count ++;
201    }
202    return;
203}
204/* }}} */
205static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
206{
207    xc_entry_t **pp = &(xce->cache->entries[xce->hvalue]);
208    xc_entry_t *p;
209    for (p = *pp; p; pp = &(p->next), p = p->next) {
210        if (xc_entry_equal_dmz(xce, p)) {
211            /* unlink */
212            *pp = p->next;
213            xc_entry_free_dmz(xce TSRMLS_CC);
214            return;
215        }
216    }
217    assert(0);
218}
219/* }}} */
220static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
221{
222    xc_entry_t *p;
223    for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
224        if (xc_entry_equal_dmz(xce, p)) {
225            if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
226                p->hits ++;
227                p->atime = XG(request_time);
228                return p;
229            }
230            else {
231                xc_entry_remove_dmz(p TSRMLS_CC);
232                return NULL;
233            }
234        }
235    }
236    return NULL;
237}
238/* }}} */
239static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
240{
241#ifdef DEBUG
242    fprintf(stderr, "hold %s\n", ZSTR_S(xce->name));
243#endif
244    xce->refcount ++;
245    xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
246}
247/* }}} */
248#if 0
249static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
250{
251    xce->refcount ++;
252    xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
253}
254/* }}} */
255#endif
256
257/* helper function that loop through each entry */
258#define XC_ENTRY_APPLY_FUNC(name) int name(xc_entry_t *entry TSRMLS_DC)
259typedef XC_ENTRY_APPLY_FUNC((*cache_apply_dmz_func_t));
260static void xc_entry_apply_dmz(xc_cache_t *cache, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
261{
262    xc_entry_t *p, **pp;
263    int i, c;
264
265    for (i = 0, c = cache->hentry->size; i < c; i ++) {
266        pp = &(cache->entries[i]);
267        for (p = *pp; p; p = *pp) {
268            if (apply_func(p TSRMLS_CC)) {
269                /* unlink */
270                *pp = p->next;
271                xc_entry_free_dmz(p TSRMLS_CC);
272            }
273            else {
274                pp = &(p->next);
275            }
276        }
277    }
278}
279/* }}} */
280
281#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
282/* call graph:
283 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_php_entry_dmz
284 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_var_entry_dmz
285 */
286static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_dmz) /* {{{ */
287{
288#ifdef DEBUG
289    fprintf(stderr, "ttl %d, %d %d\n", XG(request_time), entry->atime, xc_php_ttl);
290#endif
291    if (XG(request_time) > entry->atime + xc_php_ttl) {
292        return 1;
293    }
294    return 0;
295}
296/* }}} */
297static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_dmz) /* {{{ */
298{
299    if (VAR_ENTRY_EXPIRED(entry)) {
300        return 1;
301    }
302    return 0;
303}
304/* }}} */
305static void xc_gc_expires_one(xc_cache_t *cache, zend_ulong gc_interval, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
306{
307#ifdef DEBUG
308    fprintf(stderr, "interval %d, %d %d\n", XG(request_time), cache->last_gc_expires, gc_interval);
309#endif
310    if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
311        ENTER_LOCK(cache) {
312            if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
313                cache->last_gc_expires = XG(request_time);
314                xc_entry_apply_dmz(cache, apply_func TSRMLS_CC);
315            }
316        } LEAVE_LOCK(cache);
317    }
318}
319/* }}} */
320static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
321{
322    int i, c;
323
324    if (!xc_php_ttl || !xc_php_gc_interval) {
325        return;
326    }
327
328    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
329        xc_gc_expires_one(xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_dmz TSRMLS_CC);
330    }
331}
332/* }}} */
333static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
334{
335    int i, c;
336
337    if (!xc_var_gc_interval) {
338        return;
339    }
340
341    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
342        xc_gc_expires_one(xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_dmz TSRMLS_CC);
343    }
344}
345/* }}} */
346
347static XC_CACHE_APPLY_FUNC(xc_gc_delete_dmz) /* {{{ */
348{
349    xc_entry_t *p, **pp;
350
351    pp = &cache->deletes;
352    for (p = *pp; p; p = *pp) {
353        if (XG(request_time) - p->dtime > 3600) {
354            p->refcount = 0;
355            /* issue warning here */
356        }
357        if (p->refcount == 0) {
358            /* unlink */
359            *pp = p->next;
360            cache->deletes_count --;
361            xc_entry_free_real_dmz(p);
362        }
363        else {
364            pp = &(p->next);
365        }
366    }
367}
368/* }}} */
369static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
370{
371    if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
372        ENTER_LOCK(cache) {
373            if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
374                cache->last_gc_deletes = XG(request_time);
375                xc_gc_delete_dmz(cache TSRMLS_CC);
376            }
377        } LEAVE_LOCK(cache);
378    }
379}
380/* }}} */
381static void xc_gc_deletes(TSRMLS_D) /* {{{ */
382{
383    int i, c;
384
385    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
386        xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
387    }
388
389    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
390        xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
391    }
392}
393/* }}} */
394
395/* helper functions for user functions */
396static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
397{
398    zval *blocks;
399    const xc_block_t *b;
400#ifndef NDEBUG
401    xc_memsize_t avail = 0;
402#endif
403    xc_mem_t *mem = cache->mem;
404    const xc_mem_handlers_t *handlers = mem->handlers;
405    zend_ulong interval = (cachetype == XC_TYPE_PHP) ? xc_php_gc_interval : xc_var_gc_interval;
406
407    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
408    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
409    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->misses);
410    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
411    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
412    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
413
414    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cache->entries_count);
415    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cache->deletes_count);
416    if (interval) {
417        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    (cache->last_gc_expires + interval) - XG(request_time));
418    }
419    else {
420        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
421    }
422
423    MAKE_STD_ZVAL(blocks);
424    array_init(blocks);
425
426    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
427    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
428    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
429
430    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
431        zval *bi;
432
433        MAKE_STD_ZVAL(bi);
434        array_init(bi);
435
436        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
437        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
438        add_next_index_zval(blocks, bi);
439#ifndef NDEBUG
440        avail += handlers->block_size(b);
441#endif
442    }
443    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
444    assert(avail == handlers->avail(mem));
445}
446/* }}} */
447static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
448{
449    zval* ei;
450    xc_entry_data_php_t *php;
451    xc_entry_data_var_t *var;
452
453    ALLOC_INIT_ZVAL(ei);
454    array_init(ei);
455
456    add_assoc_long_ex(ei, ZEND_STRS("size"),     entry->size);
457    add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
458    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
459    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
460    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
461    if (del) {
462        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
463    }
464#ifdef IS_UNICODE
465    do {
466        zval *zv;
467        ALLOC_INIT_ZVAL(zv);
468        switch (entry->name_type) {
469            case IS_UNICODE:
470                    ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
471                break;
472            case IS_STRING:
473                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
474                break;
475            default:
476                assert(0);
477        }
478        zv->type = entry->name_type;
479        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
480    } while (0);
481#else
482    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
483#endif
484    switch (entry->type) {
485        case XC_TYPE_PHP:
486            php = entry->data.php;
487            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),    php->sourcesize);
488#ifdef HAVE_INODE
489            add_assoc_long_ex(ei, ZEND_STRS("device"),        php->device);
490            add_assoc_long_ex(ei, ZEND_STRS("inode"),         php->inode);
491#endif
492            add_assoc_long_ex(ei, ZEND_STRS("mtime"),         php->mtime);
493
494#ifdef HAVE_XCACHE_CONSTANT
495            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
496#endif
497            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
498            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
499            break;
500        case XC_TYPE_VAR:
501            var = entry->data.var;
502            break;
503
504        default:
505            assert(0);
506    }
507
508    add_next_index_zval(list, ei);
509}
510/* }}} */
511static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
512{
513    zval* list;
514    int i, c;
515    xc_entry_t *e;
516
517    ALLOC_INIT_ZVAL(list);
518    array_init(list);
519
520    for (i = 0, c = cache->hentry->size; i < c; i ++) {
521        for (e = cache->entries[i]; e; e = e->next) {
522            xc_fillentry_dmz(e, 0, list TSRMLS_CC);
523        }
524    }
525    add_assoc_zval(return_value, "cache_list", list);
526
527    ALLOC_INIT_ZVAL(list);
528    array_init(list);
529    for (e = cache->deletes; e; e = e->next) {
530        xc_fillentry_dmz(e, 1, list TSRMLS_CC);
531    }
532    add_assoc_zval(return_value, "deleted_list", list);
533}
534/* }}} */
535
536static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
537{
538    zend_uint i;
539    xc_entry_data_php_t *p = xce->data.php;
540#ifndef ZEND_ENGINE_2
541    /* new ptr which is stored inside CG(class_table) */
542    xc_cest_t **new_cest_ptrs = (xc_cest_t **)do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt);
543#endif
544
545    CG(active_op_array) = p->op_array;
546
547#ifdef HAVE_XCACHE_CONSTANT
548    /* install constant */
549    for (i = 0; i < p->constinfo_cnt; i ++) {
550        xc_constinfo_t *ci = &p->constinfos[i];
551        xc_install_constant(xce->name.str.val, &ci->constant,
552                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
553    }
554#endif
555
556    /* install function */
557    for (i = 0; i < p->funcinfo_cnt; i ++) {
558        xc_funcinfo_t  *fi = &p->funcinfos[i];
559        xc_install_function(xce->name.str.val, &fi->func,
560                UNISW(0, fi->type), fi->key, fi->key_size TSRMLS_CC);
561    }
562
563    /* install class */
564    for (i = 0; i < p->classinfo_cnt; i ++) {
565        xc_classinfo_t *ci = &p->classinfos[i];
566#ifndef ZEND_ENGINE_2
567        zend_class_entry *ce = CestToCePtr(ci->cest);
568        /* fix pointer to the be which inside class_table */
569        if (ce->parent) {
570            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
571            assert(class_idx < i);
572            ci->cest.parent = new_cest_ptrs[class_idx];
573        }
574        new_cest_ptrs[i] =
575#endif
576        xc_install_class(xce->name.str.val, &ci->cest, ci->oplineno,
577                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
578    }
579
580    i = 1;
581    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
582    if (h) {
583        zend_llist_add_element(&CG(open_files), h);
584    }
585
586#ifndef ZEND_ENGINE_2
587    free_alloca(new_cest_ptrs);
588#endif
589    return p->op_array;
590}
591/* }}} */
592
593static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
594{
595    int i;
596    xc_stack_t *s;
597    xc_cache_t *cache;
598    xc_entry_t *xce;
599
600    for (i = 0; i < cachecount; i ++) {
601        s = &holds[i];
602#ifdef DEBUG
603        fprintf(stderr, "holded %d\n", xc_stack_size(s));
604#endif
605        if (xc_stack_size(s)) {
606            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
607            ENTER_LOCK(cache) {
608                while (xc_stack_size(s)) {
609                    xce = (xc_entry_t*) xc_stack_pop(s);
610#ifdef DEBUG
611                    fprintf(stderr, "unhold %s\n", ZSTR_S(xce->name));
612#endif
613                    xce->refcount --;
614                    assert(xce->refcount >= 0);
615                }
616            } LEAVE_LOCK(cache);
617        }
618    }
619}
620/* }}} */
621static void xc_entry_unholds(TSRMLS_D) /* {{{ */
622{
623    xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
624    xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
625}
626/* }}} */
627static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
628{
629    char filepath[MAXPATHLEN];
630    char *paths, *path;
631    char *tokbuf;
632    int size = strlen(include_path) + 1;
633    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
634
635    paths = (char *)do_alloca(size);
636    memcpy(paths, include_path, size);
637
638    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
639        if (snprintf(filepath, sizeof(filepath), "%s/%s", path, filename) >= MAXPATHLEN - 1) {
640            continue;
641        }
642        if (VCWD_STAT(filepath, pbuf) == 0) {
643            free_alloca(paths);
644            return 0;
645        }
646    }
647
648    free_alloca(paths);
649
650    return 1;
651}
652/* }}} */
653
654#define HASH(i) (i)
655#define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
656#define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
657#define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
658#define HASH_NUM(n) HASH(n)
659static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *xce TSRMLS_DC) /* {{{ */
660{
661    return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, xce->name.uni.val, xce->name.uni.len) :)
662        HASH_STR_L(xce->name.str.val, xce->name.str.len);
663}
664/* }}} */
665#define xc_entry_hash_var xc_entry_hash_name
666static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce TSRMLS_DC) /* {{{ */
667{
668#ifdef HAVE_INODE
669    if (xce->data.php->inode) {
670        return HASH(xce->data.php->device + xce->data.php->inode);
671    }
672#endif
673    return xc_entry_hash_name(xce TSRMLS_CC);
674}
675/* }}} */
676static int xc_entry_init_key_php(xc_entry_t *xce, char *filename, char *opened_path_buffer TSRMLS_DC) /* {{{ */
677{
678    struct stat buf, *pbuf;
679    xc_hash_value_t hv;
680    int cacheid;
681    xc_entry_data_php_t *php;
682    char *ptr;
683
684    if (!filename || !SG(request_info).path_translated) {
685        return 0;
686    }
687
688    php = xce->data.php;
689
690    if (XG(stat)) {
691        if (strcmp(SG(request_info).path_translated, filename) == 0) {
692            /* sapi has already done this stat() for us */
693            pbuf = sapi_get_stat(TSRMLS_C);
694            if (pbuf) {
695                goto stat_done;
696            }
697        }
698
699        /* absolute path */
700        pbuf = &buf;
701        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
702            if (VCWD_STAT(filename, pbuf) != 0) {
703                return 0;
704            }
705            goto stat_done;
706        }
707
708        /* relative path */
709        if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
710            ptr = filename + 1;
711            if (*ptr == '.') {
712                while (*(++ptr) == '.');
713                if (!IS_SLASH(*ptr)) {
714                    goto not_relative_path;
715                }   
716            }
717
718            if (VCWD_STAT(filename, pbuf) != 0) {
719                return 0;
720            }
721            goto stat_done;
722        }
723not_relative_path:
724
725        /* use include_path */
726        if (xc_stat(filename, PG(include_path), pbuf TSRMLS_CC) != 0) {   
727            return 0;
728        }
729
730        /* fall */
731
732stat_done:
733        if (XG(request_time) - pbuf->st_mtime < 2 && !xc_test) {
734            return 0;
735        }
736
737        php->mtime        = pbuf->st_mtime;
738#ifdef HAVE_INODE
739        php->device       = pbuf->st_dev;
740        php->inode        = pbuf->st_ino;
741#endif
742        php->sourcesize   = pbuf->st_size;
743    }
744    else { /* XG(inode) */
745        php->mtime        = 0;
746#ifdef HAVE_INODE
747        php->device       = 0;
748        php->inode        = 0;
749#endif
750        php->sourcesize   = 0;
751    }
752
753#ifdef HAVE_INODE
754    if (!php->inode)
755#endif
756    {
757        /* hash on filename, let's expand it to real path */
758        filename = expand_filepath(filename, opened_path_buffer TSRMLS_CC);
759        if (filename == NULL) {
760            return 0;
761        }
762    }
763
764    UNISW(NOTHING, xce->name_type = IS_STRING;)
765    xce->name.str.val = filename;
766    xce->name.str.len = strlen(filename);
767
768    hv = xc_entry_hash_php(xce TSRMLS_CC);
769    cacheid = (hv & xc_php_hcache.mask);
770    xce->cache = xc_php_caches[cacheid];
771    hv >>= xc_php_hcache.bits;
772    xce->hvalue = (hv & xc_php_hentry.mask);
773
774    xce->type = XC_TYPE_PHP;
775    return 1;
776}
777/* }}} */
778static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
779{
780    char *class_name;
781    int i, class_len;
782    xc_cest_t cest;
783    xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
784
785    class_name = opline->op1.u.constant.value.str.val;
786    class_len  = opline->op1.u.constant.value.str.len;
787    if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
788        assert(0);
789    }
790#ifdef DEBUG
791    fprintf(stderr, "got ZEND_DECLARE_INHERITED_CLASS: %s\n", class_name + 1);
792#endif
793    /* let's see which class */
794    for (i = 0; i < php->classinfo_cnt; i ++) {
795        if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
796            php->classinfos[i].oplineno = oplineno;
797            php->have_early_binding = 1;
798            break;
799        }
800    }
801
802    if (i == php->classinfo_cnt) {
803        assert(0);
804    }
805}
806/* }}} */
807static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
808{
809    xc_sandbox_t sandbox;
810    zend_op_array *op_array;
811    xc_entry_t xce, *stored_xce;
812    xc_entry_data_php_t php;
813    xc_cache_t *cache;
814    zend_bool clogged = 0;
815    zend_bool catched = 0;
816    char *filename;
817    char opened_path_buffer[MAXPATHLEN];
818    int old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
819    int i;
820
821    if (!xc_initized) {
822        assert(0);
823    }
824
825    if (!XG(cacher)) {
826        op_array = origin_compile_file(h, type TSRMLS_CC);
827#ifdef HAVE_XCACHE_OPTIMIZER
828        if (XG(optimizer)) {
829            xc_optimize(op_array TSRMLS_CC);
830        }
831#endif
832        return op_array;
833    }
834
835    /* {{{ prepare key
836     * include_once() and require_once() gives us opened_path
837     * however, include() and require() non-absolute path which break
838     * included_files, and may confuse with (include|require)_once
839     * -- Xuefer
840     */
841
842    filename = h->opened_path ? h->opened_path : h->filename;
843    xce.data.php = &php;
844    if (!xc_entry_init_key_php(&xce, filename, opened_path_buffer TSRMLS_CC)) {
845        return origin_compile_file(h, type TSRMLS_CC);
846    }
847    cache = xce.cache;
848    /* }}} */
849    /* {{{ restore */
850    /* stale precheck */
851    if (cache->compiling) {
852        cache->clogs ++; /* is it safe here? */
853        return origin_compile_file(h, type TSRMLS_CC);
854    }
855
856    stored_xce = NULL;
857    op_array = NULL;
858    ENTER_LOCK_EX(cache) {
859        /* clogged */
860        if (cache->compiling) {
861            cache->clogs ++;
862            op_array = NULL;
863            clogged = 1;
864            break;
865        }
866
867        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
868        /* found */
869        if (stored_xce) {
870#ifdef DEBUG
871            fprintf(stderr, "found %s, catch it\n", stored_xce->name.str.val);
872#endif
873            xc_entry_hold_php_dmz(stored_xce TSRMLS_CC);
874            cache->hits ++;
875            break;
876        }
877
878        cache->compiling = XG(request_time);
879        cache->misses ++;
880    } LEAVE_LOCK_EX(cache);
881
882    if (catched) {
883        cache->compiling = 0;
884        zend_bailout();
885    }
886
887    /* found */
888    if (stored_xce) {
889        goto restore;
890    }
891
892    /* clogged */
893    if (clogged) {
894        return origin_compile_file(h, type TSRMLS_CC);
895    }
896    /* }}} */
897
898    /* {{{ compile */
899#ifdef DEBUG
900    fprintf(stderr, "compiling %s\n", filename);
901#endif
902
903    /* make compile inside sandbox */
904    xc_sandbox_init(&sandbox, filename TSRMLS_CC);
905
906    old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
907    old_funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
908    old_constinfo_cnt  = zend_hash_num_elements(EG(zend_constants));
909
910    zend_try {
911        op_array = origin_compile_file(h, type TSRMLS_CC);
912    } zend_catch {
913        catched = 1;
914    } zend_end_try();
915
916    if (catched) {
917        goto err_bailout;
918    }
919
920    if (op_array == NULL) {
921        goto err_oparray;
922    }
923
924    filename = h->opened_path ? h->opened_path : h->filename;
925    /* none-inode enabled entry hash/compare on name
926     * do not update to its name to real pathname
927     */
928#ifdef HAVE_INODE
929    if (xce.data.php->inode)
930    {
931        if (xce.name.str.val != filename) {
932            xce.name.str.val = filename;
933            xce.name.str.len = strlen(filename);
934        }
935    }
936#endif
937
938#ifdef HAVE_XCACHE_OPTIMIZER
939    if (XG(optimizer)) {
940        xc_optimize(op_array TSRMLS_CC);
941    }
942#endif
943    /* }}} */
944    /* {{{ prepare */
945    php.op_array      = op_array;
946
947#ifdef HAVE_XCACHE_CONSTANT
948    php.constinfo_cnt = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
949#endif
950    php.funcinfo_cnt  = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
951    php.classinfo_cnt = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
952
953#define X_ALLOC_N(var, cnt) do {     \
954    if (php.cnt) {                   \
955        ECALLOC_N(php.var, php.cnt); \
956        if (!php.var) {              \
957            goto err_##var;          \
958        }                            \
959    }                                \
960    else {                           \
961        php.var = NULL;              \
962    }                                \
963} while (0)
964
965#ifdef HAVE_XCACHE_CONSTANT
966    X_ALLOC_N(constinfos, constinfo_cnt);
967#endif
968    X_ALLOC_N(funcinfos,  funcinfo_cnt);
969    X_ALLOC_N(classinfos, classinfo_cnt);
970#undef X_ALLOC
971    /* }}} */
972    /* {{{ shallow copy, pointers only */ {
973        Bucket *b;
974        unsigned int i;
975
976#define COPY_H(vartype, var, cnt, name, datatype) do {        \
977    for (i = 0; b; i ++, b = b->pListNext) {                  \
978        vartype *data = &php.var[i];                          \
979                                                              \
980        if (i < old_##cnt) {                                  \
981            continue;                                         \
982        }                                                     \
983                                                              \
984        assert(i < old_##cnt + php.cnt);                      \
985        assert(b->pData);                                     \
986        memcpy(&data->name, b->pData, sizeof(datatype));      \
987        UNISW(NOTHING, data->type = b->key.type;)             \
988        if (UNISW(1, b->key.type == IS_STRING)) {             \
989            ZSTR_S(data->key)      = BUCKET_KEY_S(b);          \
990        }                                                     \
991        else {                                                \
992            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
993        }                                                     \
994        data->key_size   = b->nKeyLength;                     \
995    }                                                         \
996} while(0)
997
998#ifdef HAVE_XCACHE_CONSTANT
999        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1000#endif
1001        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1002        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1003
1004#undef COPY_H
1005        /* for ZE1, cest need to fix inside store */
1006    }
1007    /* }}} */
1008    /* {{{ find inherited classes that should be early-binding */
1009    php.have_early_binding = 0;
1010    for (i = 0; i < php.classinfo_cnt; i ++) {
1011        php.classinfos[i].oplineno = -1;
1012    }
1013
1014    xc_undo_pass_two(php.op_array TSRMLS_CC);
1015    xc_foreach_early_binding_class(php.op_array, xc_cache_early_binding_class_cb, (void *) &php TSRMLS_CC);
1016    xc_redo_pass_two(php.op_array TSRMLS_CC);
1017    /* }}} */
1018#ifdef SHOW_DPRINT
1019    xc_dprint(&xce, 0 TSRMLS_CC);
1020#endif
1021    ENTER_LOCK_EX(cache) { /* {{{ store/add entry */
1022        stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
1023    } LEAVE_LOCK_EX(cache);
1024    /* }}} */
1025#ifdef DEBUG
1026    fprintf(stderr, "stored\n");
1027#endif
1028
1029#define X_FREE(var) \
1030    if (xce.data.php->var) { \
1031        efree(xce.data.php->var); \
1032    } \
1033err_##var:
1034
1035    X_FREE(classinfos)
1036    X_FREE(funcinfos)
1037#ifdef HAVE_XCACHE_CONSTANT
1038    X_FREE(constinfos)
1039#endif
1040#undef X_FREE
1041
1042err_oparray:
1043err_bailout:
1044
1045    if (xc_test && stored_xce) {
1046        /* free it, no install. restore now */
1047        xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1048    }
1049    else if (!op_array) {
1050        /* failed to compile free it, no install */
1051        xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1052    }
1053    else {
1054        CG(active_op_array) = op_array;
1055        xc_sandbox_free(&sandbox, 1 TSRMLS_CC);
1056    }
1057
1058    ENTER_LOCK(cache) {
1059        cache->compiling = 0;
1060    } LEAVE_LOCK(cache);
1061    if (catched) {
1062        zend_bailout();
1063    }
1064    if (xc_test && stored_xce) {
1065#ifdef ZEND_ENGINE_2
1066        destroy_op_array(op_array TSRMLS_CC);
1067#else
1068        destroy_op_array(op_array);
1069#endif
1070        efree(op_array);
1071        h = NULL;
1072        goto restore;
1073    }
1074    return op_array;
1075
1076restore:
1077    CG(in_compilation)    = 1;
1078    CG(compiled_filename) = stored_xce->name.str.val;
1079    CG(zend_lineno)       = 0;
1080#ifdef DEBUG
1081    fprintf(stderr, "restoring\n");
1082#endif
1083    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
1084#ifdef SHOW_DPRINT
1085    xc_dprint(&xce, 0 TSRMLS_CC);
1086#endif
1087
1088    catched = 0;
1089    zend_try {
1090        op_array = xc_entry_install(&xce, h TSRMLS_CC);
1091    } zend_catch {
1092        catched = 1;
1093    } zend_end_try();
1094
1095#define X_FREE(var) \
1096    if (xce.data.php->var) { \
1097        efree(xce.data.php->var); \
1098    }
1099    X_FREE(classinfos)
1100    X_FREE(funcinfos)
1101#ifdef HAVE_XCACHE_CONSTANT
1102    X_FREE(constinfos)
1103#endif
1104#undef X_FREE
1105    efree(xce.data.php);
1106
1107    if (catched) {
1108        zend_bailout();
1109    }
1110    CG(in_compilation)    = 0;
1111    CG(compiled_filename) = NULL;
1112#ifdef DEBUG
1113    fprintf(stderr, "restored\n");
1114#endif
1115    return op_array;
1116}
1117/* }}} */
1118
1119/* gdb helper functions, but N/A for coredump */
1120int xc_is_rw(const void *p) /* {{{ */
1121{
1122    xc_shm_t *shm;
1123    int i;
1124    if (!xc_initized) {
1125        return 0;
1126    }
1127    for (i = 0; i < xc_php_hcache.size; i ++) {
1128        shm = xc_php_caches[i]->shm;
1129        if (shm->handlers->is_readwrite(shm, p)) {
1130            return 1;
1131        }
1132    }
1133    for (i = 0; i < xc_var_hcache.size; i ++) {
1134        shm = xc_var_caches[i]->shm;
1135        if (shm->handlers->is_readwrite(shm, p)) {
1136            return 1;
1137        }
1138    }
1139    return 0;
1140}
1141/* }}} */
1142int xc_is_ro(const void *p) /* {{{ */
1143{
1144    xc_shm_t *shm;
1145    int i;
1146    if (!xc_initized) {
1147        return 0;
1148    }
1149    for (i = 0; i < xc_php_hcache.size; i ++) {
1150        shm = xc_php_caches[i]->shm;
1151        if (shm->handlers->is_readonly(shm, p)) {
1152            return 1;
1153        }
1154    }
1155    for (i = 0; i < xc_var_hcache.size; i ++) {
1156        shm = xc_var_caches[i]->shm;
1157        if (shm->handlers->is_readonly(shm, p)) {
1158            return 1;
1159        }
1160    }
1161    return 0;
1162}
1163/* }}} */
1164int xc_is_shm(const void *p) /* {{{ */
1165{
1166    return xc_is_ro(p) || xc_is_rw(p);
1167}
1168/* }}} */
1169
1170/* module helper function */
1171static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
1172{
1173    typedef struct {
1174        const char *prefix;
1175        zend_uchar (*getsize)();
1176        const char *(*get)(zend_uchar i);
1177    } xc_meminfo_t;
1178    xc_meminfo_t nameinfos[] = {
1179        { "",        xc_get_op_type_count,   xc_get_op_type   },
1180        { "",        xc_get_data_type_count, xc_get_data_type },
1181        { "",        xc_get_opcode_count,    xc_get_opcode    },
1182        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
1183        { NULL, NULL, NULL }
1184    };
1185    xc_meminfo_t* p;
1186    zend_uchar i, count;
1187    char const_name[96];
1188    int const_name_len;
1189    int undefdone = 0;
1190
1191    for (p = nameinfos; p->getsize; p ++) {
1192        count = p->getsize();
1193        for (i = 0; i < count; i ++) {
1194            const char *name = p->get(i);
1195            if (!name) continue;
1196            if (strcmp(name, "UNDEF") == 0) {
1197                if (undefdone) continue;
1198                undefdone = 1;
1199            }
1200            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
1201            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1202        }
1203    }
1204
1205    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1206    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1207    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1208    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1209    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1210    return 0;
1211}
1212/* }}} */
1213static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
1214{
1215    int i;
1216    xc_cache_t *cache;
1217    xc_shm_t *shm;
1218
1219    if (!caches) {
1220        return NULL;
1221    }
1222    shm = NULL;
1223    for (i = 0; i < hcache->size; i ++) {
1224        cache = caches[i];
1225        if (cache) {
1226            if (cache->lck) {
1227                xc_lock_destroy(cache->lck);
1228            }
1229            /* do NOT free
1230            if (cache->entries) {
1231                cache->mem->handlers->free(cache->mem, cache->entries);
1232            }
1233            cache->mem->handlers->free(cache->mem, cache);
1234            */
1235            shm = cache->shm;
1236            shm->handlers->memdestroy(cache->mem);
1237        }
1238    }
1239    free(caches);
1240    return shm;
1241}
1242/* }}} */
1243static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
1244{
1245    xc_cache_t **caches = NULL, *cache;
1246    xc_mem_t *mem;
1247    time_t now = time(NULL);
1248    int i;
1249    xc_memsize_t memsize;
1250
1251    memsize = shmsize / hcache->size;
1252
1253    /* Don't let it break out of mem after ALIGNed
1254     * This is important for
1255     * Simply loop until it fit our need
1256     */
1257    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
1258        if (memsize < ALIGN(1)) {
1259            CHECK(NULL, "cache too small");
1260        }
1261        memsize --;
1262    }
1263
1264    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1265
1266    for (i = 0; i < hcache->size; i ++) {
1267        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
1268        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
1269        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
1270        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
1271
1272        cache->hcache  = hcache;
1273        cache->hentry  = hentry;
1274        cache->shm     = shm;
1275        cache->mem     = mem;
1276        cache->cacheid = i;
1277        cache->last_gc_deletes = now;
1278        cache->last_gc_expires = now;
1279        caches[i] = cache;
1280    }
1281    return caches;
1282
1283err:
1284    if (caches) {
1285        xc_cache_destroy(caches, hcache);
1286    }
1287    return NULL;
1288}
1289/* }}} */
1290static void xc_destroy() /* {{{ */
1291{
1292    xc_shm_t *shm = NULL;
1293
1294    if (origin_compile_file) {
1295        zend_compile_file = origin_compile_file;
1296        origin_compile_file = NULL;
1297    }
1298
1299    if (xc_php_caches) {
1300        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
1301        xc_php_caches = NULL;
1302    }
1303    if (xc_var_caches) {
1304        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1305        xc_var_caches = NULL;
1306    }
1307    if (shm) {
1308        xc_shm_destroy(shm);
1309    }
1310}
1311/* }}} */
1312static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1313{
1314    xc_shm_t *shm;
1315
1316    xc_php_caches = xc_var_caches = NULL;
1317
1318    if (xc_php_size || xc_var_size) {
1319        CHECK(shm = xc_shm_init(xc_shm_scheme, ALIGN(xc_php_size) + ALIGN(xc_var_size), xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
1320        if (!shm->handlers->can_readonly(shm)) {
1321            xc_readonly_protection = 0;
1322        }
1323
1324        if (xc_php_size) {
1325            origin_compile_file = zend_compile_file;
1326            zend_compile_file = xc_compile_file;
1327
1328            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
1329        }
1330
1331        if (xc_var_size) {
1332            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
1333        }
1334    }
1335    return 1;
1336
1337err:
1338    if (xc_php_caches || xc_var_caches) {
1339        xc_destroy();
1340        /* shm destroied */
1341    }
1342    else if (shm) {
1343        xc_shm_destroy(shm);
1344    }
1345    return 0;
1346}
1347/* }}} */
1348static void xc_request_init(TSRMLS_D) /* {{{ */
1349{
1350    int i;
1351
1352    if (xc_php_hcache.size && !XG(php_holds)) {
1353        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1354        for (i = 0; i < xc_php_hcache.size; i ++) {
1355            xc_stack_init(&XG(php_holds[i]));
1356        }
1357    }
1358
1359    if (xc_var_hcache.size && !XG(var_holds)) {
1360        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1361        for (i = 0; i < xc_var_hcache.size; i ++) {
1362            xc_stack_init(&XG(var_holds[i]));
1363        }
1364    }
1365
1366    if (XG(cacher)) {
1367#if PHP_API_VERSION <= 20041225
1368        XG(request_time) = time(NULL);
1369#else
1370        XG(request_time) = sapi_get_request_time(TSRMLS_C);
1371#endif
1372    }
1373#ifdef HAVE_XCACHE_COVERAGER
1374    xc_coverager_request_init(TSRMLS_C);
1375#endif
1376}
1377/* }}} */
1378static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1379{
1380    xc_entry_unholds(TSRMLS_C);
1381    xc_gc_expires_php(TSRMLS_C);
1382    xc_gc_expires_var(TSRMLS_C);
1383    xc_gc_deletes(TSRMLS_C);
1384#ifdef HAVE_XCACHE_COVERAGER
1385    xc_coverager_request_shutdown(TSRMLS_C);
1386#endif
1387}
1388/* }}} */
1389/* {{{ PHP_GINIT_FUNCTION(xcache) */
1390static
1391#ifdef PHP_GINIT_FUNCTION
1392PHP_GINIT_FUNCTION(xcache)
1393#else
1394void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1395#endif
1396{
1397    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
1398}
1399/* }}} */
1400/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
1401static
1402#ifdef PHP_GSHUTDOWN_FUNCTION
1403PHP_GSHUTDOWN_FUNCTION(xcache)
1404#else
1405void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1406#endif
1407{
1408    int i;
1409
1410    if (xcache_globals->php_holds != NULL) {
1411        for (i = 0; i < xc_php_hcache.size; i ++) {
1412            xc_stack_destroy(&xcache_globals->php_holds[i]);
1413        }
1414        free(xcache_globals->php_holds);
1415        xcache_globals->php_holds = NULL;
1416    }
1417
1418    if (xcache_globals->var_holds != NULL) {
1419        for (i = 0; i < xc_var_hcache.size; i ++) {
1420            xc_stack_destroy(&xcache_globals->var_holds[i]);
1421        }
1422        free(xcache_globals->var_holds);
1423        xcache_globals->var_holds = NULL;
1424    }
1425}
1426/* }}} */
1427
1428/* user functions */
1429static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
1430{
1431    zval **server = NULL;
1432    zval **user = NULL;
1433    zval **pass = NULL;
1434    char *admin_user = NULL;
1435    char *admin_pass = NULL;
1436    HashTable *ht;
1437
1438    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1439        admin_user = NULL;
1440    }
1441    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1442        admin_pass = NULL;
1443    }
1444
1445    if (admin_user == NULL || admin_pass == NULL) {
1446        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1447        zend_bailout();
1448    }
1449    if (strlen(admin_pass) != 32) {
1450        php_error_docref(NULL TSRMLS_CC, E_ERROR, "unexpect %d bytes of xcache.admin.pass, expected 32 bytes, the password after md5()", strlen(admin_pass));
1451        zend_bailout();
1452    }
1453
1454#ifdef ZEND_ENGINE_2_1
1455    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
1456#endif
1457    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1458        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1459        zend_bailout();
1460    }
1461    ht = HASH_OF((*server));
1462
1463    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1464        user = NULL;
1465    }
1466    else if (Z_TYPE_PP(user) != IS_STRING) {
1467        user = NULL;
1468    }
1469
1470    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1471        pass = NULL;
1472    }
1473    else if (Z_TYPE_PP(pass) != IS_STRING) {
1474        pass = NULL;
1475    }
1476
1477    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1478        PHP_MD5_CTX context;
1479        char md5str[33];
1480        unsigned char digest[16];
1481
1482        PHP_MD5Init(&context);
1483        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
1484        PHP_MD5Final(digest, &context);
1485
1486        md5str[0] = '\0';
1487        make_digest(md5str, digest);
1488        if (strcmp(admin_pass, md5str) == 0) {
1489            return 1;
1490        }
1491    }
1492
1493#define STR "WWW-authenticate: basic realm='XCache Administration'"
1494    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1495#undef STR
1496#define STR "HTTP/1.0 401 Unauthorized"
1497    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1498#undef STR
1499    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1500
1501    zend_bailout();
1502    return 0;
1503}
1504/* }}} */
1505/* {{{ xcache_admin_operate */
1506typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
1507static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
1508{
1509    long type;
1510    int size;
1511    xc_cache_t **caches, *cache;
1512    long id = 0;
1513
1514    if (!xc_initized) {
1515        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache is not initized");
1516        RETURN_FALSE;
1517    }
1518
1519    xcache_admin_auth_check(TSRMLS_C);
1520
1521    if (optype == XC_OP_COUNT) {
1522        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1523            return;
1524        }
1525    }
1526    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1527        return;
1528    }
1529
1530    switch (type) {
1531        case XC_TYPE_PHP:
1532            size = xc_php_hcache.size;
1533            caches = xc_php_caches;
1534            break;
1535
1536        case XC_TYPE_VAR:
1537            size = xc_var_hcache.size;
1538            caches = xc_var_caches;
1539            break;
1540
1541        default:
1542            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1543            RETURN_FALSE;
1544    }
1545
1546    switch (optype) {
1547        case XC_OP_COUNT:
1548            RETURN_LONG(size)
1549            break;
1550
1551        case XC_OP_INFO:
1552        case XC_OP_LIST:
1553            if (id < 0 || id >= size) {
1554                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1555                RETURN_FALSE;
1556            }
1557
1558            array_init(return_value);
1559
1560            cache = caches[id];
1561            ENTER_LOCK(cache) {
1562                if (optype == XC_OP_INFO) {
1563                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
1564                }
1565                else {
1566                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1567                }
1568            } LEAVE_LOCK(cache);
1569            break;
1570        case XC_OP_CLEAR:
1571            {
1572                xc_entry_t *e, *next;
1573                int i, c;
1574
1575                if (id < 0 || id >= size) {
1576                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1577                    RETURN_FALSE;
1578                }
1579
1580                cache = caches[id];
1581                ENTER_LOCK(cache) {
1582                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
1583                        for (e = cache->entries[i]; e; e = next) {
1584                            next = e->next;
1585                            xc_entry_remove_dmz(e TSRMLS_CC);
1586                        }
1587                        cache->entries[i] = NULL;
1588                    }
1589                } LEAVE_LOCK(cache);
1590                xc_gc_deletes(TSRMLS_C);
1591            }
1592            break;
1593
1594        default:
1595            assert(0);
1596    }
1597}
1598/* }}} */
1599/* {{{ proto int xcache_count(int type)
1600   Return count of cache on specified cache type */
1601PHP_FUNCTION(xcache_count)
1602{
1603    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1604}
1605/* }}} */
1606/* {{{ proto array xcache_info(int type, int id)
1607   Get cache info by id on specified cache type */
1608PHP_FUNCTION(xcache_info)
1609{
1610    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1611}
1612/* }}} */
1613/* {{{ proto array xcache_list(int type, int id)
1614   Get cache entries list by id on specified cache type */
1615PHP_FUNCTION(xcache_list)
1616{
1617    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1618}
1619/* }}} */
1620/* {{{ proto array xcache_clear_cache(int type, int id)
1621   Clear cache by id on specified cache type */
1622PHP_FUNCTION(xcache_clear_cache)
1623{
1624    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1625}
1626/* }}} */
1627
1628static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1629{
1630    xc_hash_value_t hv;
1631    int cacheid;
1632
1633    switch (Z_TYPE_P(name)) {
1634#ifdef IS_UNICODE
1635        case IS_UNICODE:
1636#endif
1637        case IS_STRING:
1638            break;
1639        default:
1640#ifdef IS_UNICODE
1641            convert_to_text(name);
1642#else
1643            convert_to_string(name);
1644#endif
1645    }
1646#ifdef IS_UNICODE
1647    xce->name_type = name->type;
1648#endif
1649    xce->name = name->value;
1650
1651    hv = xc_entry_hash_var(xce TSRMLS_CC);
1652
1653    cacheid = (hv & xc_var_hcache.mask);
1654    xce->cache = xc_var_caches[cacheid];
1655    hv >>= xc_var_hcache.bits;
1656    xce->hvalue = (hv & xc_var_hentry.mask);
1657
1658    xce->type = XC_TYPE_VAR;
1659    return SUCCESS;
1660}
1661/* }}} */
1662/* {{{ proto mixed xcache_get(string name)
1663   Get cached data by specified name */
1664PHP_FUNCTION(xcache_get)
1665{
1666    xc_entry_t xce, *stored_xce;
1667    xc_entry_data_var_t var;
1668    zval *name;
1669
1670    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1671        return;
1672    }
1673    xce.data.var = &var;
1674    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1675
1676    ENTER_LOCK(xce.cache) {
1677        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1678        if (stored_xce) {
1679            if (XG(request_time) <= stored_xce->ctime + stored_xce->ttl) {
1680                xc_processor_restore_zval(return_value, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
1681                /* return */
1682                break;
1683            }
1684            else {
1685                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1686            }
1687        }
1688
1689        RETVAL_NULL();
1690    } LEAVE_LOCK(xce.cache);
1691}
1692/* }}} */
1693/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1694   Store data to cache by specified name */
1695PHP_FUNCTION(xcache_set)
1696{
1697    xc_entry_t xce, *stored_xce;
1698    xc_entry_data_var_t var;
1699    zval *name;
1700    zval *value;
1701
1702    xce.ttl = XG(var_ttl);
1703    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
1704        return;
1705    }
1706
1707    /* max ttl */
1708    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1709        xce.ttl = xc_var_maxttl;
1710    }
1711
1712    xce.data.var = &var;
1713    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1714
1715    ENTER_LOCK(xce.cache) {
1716        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1717        if (stored_xce) {
1718            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1719        }
1720        var.value = value;
1721        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
1722    } LEAVE_LOCK(xce.cache);
1723}
1724/* }}} */
1725/* {{{ proto bool  xcache_isset(string name)
1726   Check if an entry exists in cache by specified name */
1727PHP_FUNCTION(xcache_isset)
1728{
1729    xc_entry_t xce, *stored_xce;
1730    xc_entry_data_var_t var;
1731    zval *name;
1732
1733    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1734        return;
1735    }
1736    xce.data.var = &var;
1737    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1738
1739    ENTER_LOCK(xce.cache) {
1740        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1741        if (stored_xce) {
1742            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1743                RETVAL_TRUE;
1744                /* return */
1745                break;
1746            }
1747            else {
1748                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1749            }
1750        }
1751
1752        RETVAL_FALSE;
1753    } LEAVE_LOCK(xce.cache);
1754}
1755/* }}} */
1756/* {{{ proto bool  xcache_unset(string name)
1757   Unset existing data in cache by specified name */
1758PHP_FUNCTION(xcache_unset)
1759{
1760    xc_entry_t xce, *stored_xce;
1761    xc_entry_data_var_t var;
1762    zval *name;
1763
1764    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1765        return;
1766    }
1767    xce.data.var = &var;
1768    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1769
1770    ENTER_LOCK(xce.cache) {
1771        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1772        if (stored_xce) {
1773            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1774            RETVAL_TRUE;
1775        }
1776        else {
1777            RETVAL_FALSE;
1778        }
1779    } LEAVE_LOCK(xce.cache);
1780}
1781/* }}} */
1782static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1783{
1784    xc_entry_t xce, *stored_xce;
1785    xc_entry_data_var_t var, *stored_var;
1786    zval *name;
1787    long count = 1;
1788    long value = 0;
1789    zval oldzval;
1790
1791    xce.ttl = XG(var_ttl);
1792    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
1793        return;
1794    }
1795
1796    /* max ttl */
1797    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1798        xce.ttl = xc_var_maxttl;
1799    }
1800
1801    xce.data.var = &var;
1802    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1803
1804    ENTER_LOCK(xce.cache) {
1805        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1806        if (stored_xce) {
1807#ifdef DEBUG
1808            fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
1809#endif
1810            /* timeout */
1811            if (VAR_ENTRY_EXPIRED(stored_xce)) {
1812#ifdef DEBUG
1813                fprintf(stderr, "incdec: expired\n");
1814#endif
1815                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1816                stored_xce = NULL;
1817            }
1818            else {
1819                /* do it in place */
1820                stored_var = stored_xce->data.var;
1821                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
1822                    stored_xce->ctime = XG(request_time);
1823                    stored_xce->ttl   = xce.ttl;
1824#ifdef DEBUG
1825                    fprintf(stderr, "incdec: islong\n");
1826#endif
1827                    value = Z_LVAL_P(stored_var->value);
1828                    value += (inc == 1 ? count : - count);
1829                    RETVAL_LONG(value);
1830                    Z_LVAL_P(stored_var->value) = value;
1831                    break; /* leave lock */
1832                }
1833                else {
1834#ifdef DEBUG
1835                    fprintf(stderr, "incdec: notlong\n");
1836#endif
1837                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
1838                    convert_to_long(&oldzval);
1839                    value = Z_LVAL(oldzval);
1840                    zval_dtor(&oldzval);
1841                }
1842            }
1843        }
1844#ifdef DEBUG
1845        else {
1846            fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
1847        }
1848#endif
1849
1850        value += (inc == 1 ? count : - count);
1851        RETVAL_LONG(value);
1852        var.value = return_value;
1853
1854        if (stored_xce) {
1855            xce.atime = stored_xce->atime;
1856            xce.ctime = stored_xce->ctime;
1857            xce.hits  = stored_xce->hits;
1858            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1859        }
1860        xc_entry_store_dmz(&xce TSRMLS_CC);
1861
1862    } LEAVE_LOCK(xce.cache);
1863}
1864/* }}} */
1865/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
1866   Increase an int counter in cache by specified name, create it if not exists */
1867PHP_FUNCTION(xcache_inc)
1868{
1869    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1870}
1871/* }}} */
1872/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
1873   Decrease an int counter in cache by specified name, create it if not exists */
1874PHP_FUNCTION(xcache_dec)
1875{
1876    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1877}
1878/* }}} */
1879/* {{{ proto string xcache_asm(string filename)
1880 */
1881#ifdef HAVE_XCACHE_ASSEMBLER
1882PHP_FUNCTION(xcache_asm)
1883{
1884}
1885#endif
1886/* }}} */
1887#ifdef HAVE_XCACHE_DISASSEMBLER
1888/* {{{ proto array  xcache_dasm_file(string filename)
1889   Disassemble file into opcode array by filename */
1890PHP_FUNCTION(xcache_dasm_file)
1891{
1892    char *filename;
1893    int filename_len;
1894
1895    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
1896        return;
1897    }
1898    if (!filename_len) RETURN_FALSE;
1899
1900    xc_dasm_file(return_value, filename TSRMLS_CC);
1901}
1902/* }}} */
1903/* {{{ proto array  xcache_dasm_string(string code)
1904   Disassemble php code into opcode array */
1905PHP_FUNCTION(xcache_dasm_string)
1906{
1907    zval *code;
1908
1909    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
1910        return;
1911    }
1912    xc_dasm_string(return_value, code TSRMLS_CC);
1913}
1914/* }}} */
1915#endif
1916/* {{{ proto string xcache_encode(string filename)
1917   Encode php file into XCache opcode encoded format */
1918#ifdef HAVE_XCACHE_ENCODER
1919PHP_FUNCTION(xcache_encode)
1920{
1921}
1922#endif
1923/* }}} */
1924/* {{{ proto bool xcache_decode_file(string filename)
1925   Decode(load) opcode from XCache encoded format file */
1926#ifdef HAVE_XCACHE_DECODER
1927PHP_FUNCTION(xcache_decode_file)
1928{
1929}
1930#endif
1931/* }}} */
1932/* {{{ proto bool xcache_decode_string(string data)
1933   Decode(load) opcode from XCache encoded format data */
1934#ifdef HAVE_XCACHE_DECODER
1935PHP_FUNCTION(xcache_decode_string)
1936{
1937}
1938#endif
1939/* }}} */
1940/* {{{ xc_call_getter */
1941typedef const char *(xc_name_getter_t)(zend_uchar type);
1942static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
1943{
1944    long spec;
1945    const char *name;
1946
1947    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1948        return;
1949    }
1950    if (spec >= 0 && spec < count) {
1951        name = getter((zend_uchar) spec);
1952        if (name) {
1953            /* RETURN_STRING */
1954            int len = strlen(name);
1955            return_value->value.str.len = len;
1956            return_value->value.str.val = estrndup(name, len);
1957            return_value->type = IS_STRING; 
1958            return;
1959        }
1960    }
1961    RETURN_NULL();
1962}
1963/* }}} */
1964/* {{{ proto string xcache_get_op_type(int op_type) */
1965PHP_FUNCTION(xcache_get_op_type)
1966{
1967    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1968}
1969/* }}} */
1970/* {{{ proto string xcache_get_data_type(int type) */
1971PHP_FUNCTION(xcache_get_data_type)
1972{
1973    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1974}
1975/* }}} */
1976/* {{{ proto string xcache_get_opcode(int opcode) */
1977PHP_FUNCTION(xcache_get_opcode)
1978{
1979    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1980}
1981/* }}} */
1982/* {{{ proto string xcache_get_op_spec(int op_type) */
1983PHP_FUNCTION(xcache_get_op_spec)
1984{
1985    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1986}
1987/* }}} */
1988#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1989/* {{{ proto string xcache_get_opcode_spec(int opcode) */
1990PHP_FUNCTION(xcache_get_opcode_spec)
1991{
1992    long spec;
1993    const xc_opcode_spec_t *opspec;
1994
1995    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1996        return;
1997    }
1998    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
1999        opspec = xc_get_opcode_spec((zend_uchar) spec);
2000        if (opspec) {
2001            array_init(return_value);
2002            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
2003            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
2004            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
2005            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
2006            return;
2007        }
2008    }
2009    RETURN_NULL();
2010}
2011/* }}} */
2012#endif
2013/* {{{ proto mixed xcache_get_special_value(zval value) */
2014PHP_FUNCTION(xcache_get_special_value)
2015{
2016    zval *value;
2017
2018    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
2019        return;
2020    }
2021
2022    if (value->type == IS_CONSTANT) {
2023        *return_value = *value;
2024        zval_copy_ctor(return_value);
2025        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
2026        return;
2027    }
2028
2029    if (value->type == IS_CONSTANT_ARRAY) {
2030        *return_value = *value;
2031        zval_copy_ctor(return_value);
2032        return_value->type = IS_ARRAY;
2033        return;
2034    }
2035
2036    RETURN_NULL();
2037}
2038/* }}} */
2039/* {{{ proto string xcache_coredump(int op_type) */
2040PHP_FUNCTION(xcache_coredump)
2041{
2042    if (xc_test) {
2043        raise(SIGSEGV);
2044    }
2045    else {
2046        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
2047    }
2048}
2049/* }}} */
2050/* {{{ proto string xcache_is_autoglobal(string name) */
2051PHP_FUNCTION(xcache_is_autoglobal)
2052{
2053    char *name;
2054    int name_len;
2055
2056    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
2057        return;
2058    }
2059
2060    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
2061}
2062/* }}} */
2063static function_entry xcache_functions[] = /* {{{ */
2064{
2065    PHP_FE(xcache_count,             NULL)
2066    PHP_FE(xcache_info,              NULL)
2067    PHP_FE(xcache_list,              NULL)
2068    PHP_FE(xcache_clear_cache,       NULL)
2069    PHP_FE(xcache_coredump,          NULL)
2070#ifdef HAVE_XCACHE_ASSEMBLER
2071    PHP_FE(xcache_asm,               NULL)
2072#endif
2073#ifdef HAVE_XCACHE_DISASSEMBLER
2074    PHP_FE(xcache_dasm_file,         NULL)
2075    PHP_FE(xcache_dasm_string,       NULL)
2076#endif
2077#ifdef HAVE_XCACHE_ENCODER
2078    PHP_FE(xcache_encode,            NULL)
2079#endif
2080#ifdef HAVE_XCACHE_DECODER
2081    PHP_FE(xcache_decode_file,       NULL)
2082    PHP_FE(xcache_decode_string,     NULL)
2083#endif
2084#ifdef HAVE_XCACHE_COVERAGER
2085    PHP_FE(xcache_coverager_decode,  NULL)
2086    PHP_FE(xcache_coverager_start,   NULL)
2087    PHP_FE(xcache_coverager_stop,    NULL)
2088    PHP_FE(xcache_coverager_get,     NULL)
2089#endif
2090    PHP_FE(xcache_get_special_value, NULL)
2091    PHP_FE(xcache_get_op_type,       NULL)
2092    PHP_FE(xcache_get_data_type,     NULL)
2093    PHP_FE(xcache_get_opcode,        NULL)
2094#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
2095    PHP_FE(xcache_get_opcode_spec,   NULL)
2096#endif
2097    PHP_FE(xcache_is_autoglobal,     NULL)
2098    PHP_FE(xcache_inc,               NULL)
2099    PHP_FE(xcache_dec,               NULL)
2100    PHP_FE(xcache_get,               NULL)
2101    PHP_FE(xcache_set,               NULL)
2102    PHP_FE(xcache_isset,             NULL)
2103    PHP_FE(xcache_unset,             NULL)
2104    {NULL, NULL,                     NULL}
2105};
2106/* }}} */
2107
2108/* old signal handlers {{{ */
2109typedef void (*xc_sighandler_t)(int);
2110#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2111#include "foreachcoresig.h"
2112#undef FOREACH_SIG
2113/* }}} */
2114static void xcache_signal_handler(int sig);
2115static void xcache_restore_signal_handler() /* {{{ */
2116{
2117#define FOREACH_SIG(sig) do { \
2118    if (old_##sig##_handler != xcache_signal_handler) { \
2119        signal(sig, old_##sig##_handler); \
2120    } \
2121    else { \
2122        signal(sig, SIG_DFL); \
2123    } \
2124} while (0)
2125#include "foreachcoresig.h"
2126#undef FOREACH_SIG
2127}
2128/* }}} */
2129static void xcache_init_signal_handler() /* {{{ */
2130{
2131#define FOREACH_SIG(sig) \
2132    old_##sig##_handler = signal(sig, xcache_signal_handler)
2133#include "foreachcoresig.h"
2134#undef FOREACH_SIG
2135}
2136/* }}} */
2137static void xcache_signal_handler(int sig) /* {{{ */
2138{
2139    xcache_restore_signal_handler();
2140    if (xc_coredump_dir && xc_coredump_dir[0]) {
2141        chdir(xc_coredump_dir);
2142    }
2143    raise(sig);
2144}
2145/* }}} */
2146
2147/* {{{ PHP_INI */
2148
2149static PHP_INI_MH(xc_OnUpdateDummy)
2150{
2151    return SUCCESS;
2152}
2153
2154static PHP_INI_MH(xc_OnUpdateULong)
2155{
2156    zend_ulong *p = (zend_ulong *) mh_arg1;
2157
2158    *p = (zend_ulong) atoi(new_value);
2159    return SUCCESS;
2160}
2161
2162static PHP_INI_MH(xc_OnUpdateBool)
2163{
2164    zend_bool *p = (zend_bool *)mh_arg1;
2165
2166    if (strncasecmp("on", new_value, sizeof("on"))) {
2167        *p = (zend_bool) atoi(new_value);
2168    }
2169    else {
2170        *p = (zend_bool) 1;
2171    }
2172    return SUCCESS;
2173}
2174
2175static PHP_INI_MH(xc_OnUpdateString)
2176{
2177    char **p = (char**)mh_arg1;
2178    if (*p) {
2179        pefree(*p, 1);
2180    }
2181    *p = pemalloc(strlen(new_value) + 1, 1);
2182    strcpy(*p, new_value);
2183    return SUCCESS;
2184}
2185
2186#ifndef ZEND_ENGINE_2
2187#define OnUpdateLong OnUpdateInt
2188#endif
2189
2190#ifdef ZEND_WIN32
2191#   define DEFAULT_PATH "xcache"
2192#else
2193#   define DEFAULT_PATH "/dev/zero"
2194#endif
2195PHP_INI_BEGIN()
2196    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
2197    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2198    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2199    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
2200    /* opcode cache */
2201    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2202    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2203    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2204    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
2205    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
2206    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
2207    /* var cache */
2208    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2209    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2210    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2211    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
2212    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
2213
2214    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
2215    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
2216#ifdef HAVE_XCACHE_OPTIMIZER
2217    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2218#endif
2219    STD_PHP_INI_BOOLEAN("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
2220#ifdef HAVE_XCACHE_COVERAGER
2221    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
2222    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2223#endif
2224PHP_INI_END()
2225/* }}} */
2226/* {{{ PHP_MINFO_FUNCTION(xcache) */
2227static PHP_MINFO_FUNCTION(xcache)
2228{
2229    char buf[100];
2230    char *ptr;
2231    int left, len;
2232    xc_shm_scheme_t *scheme;
2233    char *covdumpdir;
2234
2235    php_info_print_table_start();
2236    php_info_print_table_header(2, "XCache Support", "enabled");
2237    php_info_print_table_row(2, "Version", XCACHE_VERSION);
2238    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
2239    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
2240
2241    if (xc_php_size) {
2242        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
2243        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
2244        php_info_print_table_row(2, "Opcode Cache", buf);
2245        efree(ptr);
2246    }
2247    else {
2248        php_info_print_table_row(2, "Opcode Cache", "disabled");
2249    }
2250    if (xc_var_size) {
2251        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
2252        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
2253        php_info_print_table_row(2, "Variable Cache", buf);
2254        efree(ptr);
2255    }
2256    else {
2257        php_info_print_table_row(2, "Variable Cache", "disabled");
2258    }
2259
2260    left = sizeof(buf);
2261    ptr = buf;
2262    buf[0] = '\0';
2263    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
2264        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
2265        left -= len;
2266        ptr += len;
2267    }
2268    php_info_print_table_row(2, "Shared Memory Schemes", buf);
2269
2270#ifdef HAVE_XCACHE_COVERAGER
2271    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
2272        covdumpdir = NULL;
2273    }
2274    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
2275#endif
2276    php_info_print_table_end();
2277
2278    DISPLAY_INI_ENTRIES();
2279}
2280/* }}} */
2281/* {{{ extension startup */
2282static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
2283{
2284    zend_extension extension;
2285
2286    extension = *new_extension;
2287    extension.handle = handle;
2288
2289    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
2290
2291    zend_llist_add_element(&zend_extensions, &extension);
2292#ifdef DEBUG
2293    fprintf(stderr, "registered\n");
2294#endif
2295}
2296
2297/* dirty check */
2298#if defined(COMPILE_DL_XCACHE) && (defined(ZEND_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__))
2299#   define zend_append_version_info(x) do { } while (0)
2300#else
2301extern void zend_append_version_info(zend_extension *extension);
2302#endif
2303static int xc_zend_extension_startup(zend_extension *extension)
2304{
2305    if (extension->startup) {
2306        if (extension->startup(extension) != SUCCESS) {
2307            return FAILURE;
2308        }
2309        zend_append_version_info(extension);
2310    }
2311    return SUCCESS;
2312}
2313/* }}} */
2314static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
2315{
2316    int bits, size;
2317    char *value;
2318
2319    if (cfg_get_string(name, &value) != SUCCESS) {
2320        value = default_value;
2321    }
2322
2323    p->size = zend_atoi(value, strlen(value));
2324    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
2325        /* empty body */
2326    }
2327    p->size = size;
2328    p->bits = bits;
2329    p->mask = size - 1;
2330
2331    return SUCCESS;
2332}
2333/* }}} */
2334static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
2335{
2336    char *value;
2337
2338    if (cfg_get_string(name, &value) != SUCCESS) {
2339        value = default_value;
2340    }
2341
2342    *p = zend_atoi(value, strlen(value));
2343    return SUCCESS;
2344}
2345/* }}} */
2346/* {{{ PHP_MINIT_FUNCTION(xcache) */
2347static PHP_MINIT_FUNCTION(xcache)
2348{
2349    char *env;
2350    zend_extension *ext;
2351    zend_llist_position lpos;
2352
2353    xc_module_gotup = 1;
2354    if (!xc_zend_extension_gotup) {
2355        if (zend_get_extension(XCACHE_NAME) == NULL) {
2356            xc_zend_extension_register(&zend_extension_entry, 0);
2357            xc_zend_extension_startup(&zend_extension_entry);
2358        }
2359    }
2360
2361    ext = zend_get_extension("Zend Optimizer");
2362    if (ext) {
2363        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
2364        ext->op_array_handler = NULL;
2365    }
2366    /* cache if there's an op_array_ctor */
2367    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
2368            ext;
2369            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
2370        if (ext->op_array_ctor) {
2371            xc_have_op_array_ctor = 1;
2372            break;
2373        }
2374    }
2375
2376
2377#ifndef PHP_GINIT
2378    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
2379#endif
2380    REGISTER_INI_ENTRIES();
2381
2382    if (strcmp(sapi_module.name, "cli") == 0) {
2383        if ((env = getenv("XCACHE_TEST")) != NULL) {
2384            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2385        }
2386        if (!xc_test) {
2387            /* disable cache for cli except for test */
2388            xc_php_size = xc_var_size = 0;
2389        }
2390    }
2391
2392    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2393    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2394    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
2395
2396    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
2397    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
2398    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
2399
2400    if (xc_php_size <= 0) {
2401        xc_php_size = xc_php_hcache.size = 0;
2402    }
2403    if (xc_var_size <= 0) {
2404        xc_var_size = xc_var_hcache.size = 0;
2405    }
2406
2407    if (xc_coredump_dir && xc_coredump_dir[0]) {
2408        xcache_init_signal_handler();
2409    }
2410
2411    xc_init_constant(module_number TSRMLS_CC);
2412    xc_shm_init_modules();
2413
2414    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
2415        if (!xc_init(module_number TSRMLS_CC)) {
2416            zend_error(E_ERROR, "XCache: Cannot init");
2417            goto err_init;
2418        }
2419        xc_initized = 1;
2420    }
2421
2422#ifdef HAVE_XCACHE_COVERAGER
2423    xc_coverager_init(module_number TSRMLS_CC);
2424#endif
2425
2426    return SUCCESS;
2427
2428err_init:
2429    return FAILURE;
2430}
2431/* }}} */
2432/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2433static PHP_MSHUTDOWN_FUNCTION(xcache)
2434{
2435    if (xc_initized) {
2436        xc_destroy();
2437        xc_initized = 0;
2438    }
2439    if (xc_mmap_path) {
2440        pefree(xc_mmap_path, 1);
2441        xc_mmap_path = NULL;
2442    }
2443    if (xc_shm_scheme) {
2444        pefree(xc_shm_scheme, 1);
2445        xc_shm_scheme = NULL;
2446    }
2447
2448#ifdef HAVE_XCACHE_COVERAGER
2449    xc_coverager_destroy();
2450#endif
2451
2452    if (xc_coredump_dir && xc_coredump_dir[0]) {
2453        xcache_restore_signal_handler();
2454    }
2455    if (xc_coredump_dir) {
2456        pefree(xc_coredump_dir, 1);
2457        xc_coredump_dir = NULL;
2458    }
2459#ifndef PHP_GINIT
2460#   ifdef ZTS
2461    ts_free_id(xcache_globals_id);
2462#   else
2463    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
2464#   endif
2465#endif
2466
2467    UNREGISTER_INI_ENTRIES();
2468    return SUCCESS;
2469}
2470/* }}} */
2471/* {{{ PHP_RINIT_FUNCTION(xcache) */
2472static PHP_RINIT_FUNCTION(xcache)
2473{
2474    xc_request_init(TSRMLS_C);
2475    return SUCCESS;
2476}
2477/* }}} */
2478/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2479#ifndef ZEND_ENGINE_2
2480static PHP_RSHUTDOWN_FUNCTION(xcache)
2481#else
2482static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2483#endif
2484{
2485#ifdef ZEND_ENGINE_2
2486    TSRMLS_FETCH();
2487#endif
2488
2489    xc_request_shutdown(TSRMLS_C);
2490    return SUCCESS;
2491}
2492/* }}} */
2493/* {{{ module definition structure */
2494
2495zend_module_entry xcache_module_entry = {
2496    STANDARD_MODULE_HEADER,
2497    "XCache",
2498    xcache_functions,
2499    PHP_MINIT(xcache),
2500    PHP_MSHUTDOWN(xcache),
2501    PHP_RINIT(xcache),
2502#ifndef ZEND_ENGINE_2
2503    PHP_RSHUTDOWN(xcache),
2504#else
2505    NULL,
2506#endif
2507    PHP_MINFO(xcache),
2508    XCACHE_VERSION,
2509#ifdef PHP_GINIT
2510    PHP_MODULE_GLOBALS(xcache),
2511    PHP_GINIT(xcache),
2512    PHP_GSHUTDOWN(xcache),
2513#endif
2514#ifdef ZEND_ENGINE_2
2515    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2516#else
2517    NULL,
2518    NULL,
2519#endif
2520    STANDARD_MODULE_PROPERTIES_EX
2521};
2522
2523#ifdef COMPILE_DL_XCACHE
2524ZEND_GET_MODULE(xcache)
2525#endif
2526/* }}} */
2527static startup_func_t xc_last_ext_startup;
2528static zend_llist_element *xc_llist_element;
2529static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
2530{
2531    return p1 == p2;
2532}
2533/* }}} */
2534static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
2535{
2536    /* restore */
2537    extension->startup = xc_last_ext_startup;
2538    if (extension->startup) {
2539        if (extension->startup(extension) != SUCCESS) {
2540            return FAILURE;
2541        }
2542    }
2543    xc_zend_extension_register(&zend_extension_entry, 0);
2544    if (!xc_module_gotup) {
2545        return zend_startup_module(&xcache_module_entry);
2546    }
2547    return SUCCESS;
2548}
2549/* }}} */
2550ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2551{
2552    if (xc_zend_extension_gotup) {
2553        return FAILURE;
2554    }
2555    xc_zend_extension_gotup = 1;
2556    xc_llist_element = NULL;
2557    if (zend_llist_count(&zend_extensions) > 1) {
2558        zend_llist_position lpos;
2559        zend_extension *ext;
2560        llist_dtor_func_t dtor;
2561
2562        ext = zend_get_extension(XCACHE_NAME);
2563        assert(ext);
2564        dtor = zend_extensions.dtor; /* avoid dtor */
2565        zend_extensions.dtor = NULL;
2566        zend_llist_del_element(&zend_extensions, ext, xc_ptr_compare_func);
2567        zend_extensions.dtor = dtor;
2568
2569        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
2570        assert(ext);
2571        xc_last_ext_startup = ext->startup;
2572        ext->startup = xc_zend_startup_last;
2573    }
2574    else if (!xc_module_gotup) {
2575        return zend_startup_module(&xcache_module_entry);
2576    }
2577    return SUCCESS;
2578}
2579/* }}} */
2580ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2581{
2582    /* empty */
2583}
2584/* }}} */
2585ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2586{
2587#ifdef HAVE_XCACHE_COVERAGER
2588    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
2589#endif
2590}
2591/* }}} */
2592ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2593{
2594#if 0
2595    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
2596#endif
2597}
2598/* }}} */
2599ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2600{
2601#if 0
2602    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
2603#endif
2604}
2605/* }}} */
2606/* {{{ zend extension definition structure */
2607ZEND_DLEXPORT zend_extension zend_extension_entry = {
2608    XCACHE_NAME,
2609    XCACHE_VERSION,
2610    XCACHE_AUTHOR,
2611    XCACHE_URL,
2612    XCACHE_COPYRIGHT,
2613    xcache_zend_startup,
2614    xcache_zend_shutdown,
2615    NULL,           /* activate_func_t */
2616    NULL,           /* deactivate_func_t */
2617    NULL,           /* message_handler_func_t */
2618    NULL,           /* op_array_handler_func_t */
2619    xcache_statement_handler,
2620    xcache_fcall_begin_handler,
2621    xcache_fcall_end_handler,
2622    NULL,           /* op_array_ctor_func_t */
2623    NULL,           /* op_array_dtor_func_t */
2624    STANDARD_ZEND_EXTENSION_PROPERTIES
2625};
2626
2627#ifndef ZEND_EXT_API
2628#   define ZEND_EXT_API ZEND_DLEXPORT
2629#endif
2630#if COMPILE_DL_XCACHE
2631ZEND_EXTENSION();
2632#endif
2633/* }}} */
Note: See TracBrowser for help on using the repository browser.