source: trunk/xcache.c @ 233

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

processor: apply reference handling on opcode caching. reverted [196] [199] [220] and refix for #36

File size: 66.4 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#ifdef DEBUG
1078    fprintf(stderr, "restoring\n");
1079#endif
1080    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
1081#ifdef SHOW_DPRINT
1082    xc_dprint(&xce, 0 TSRMLS_CC);
1083#endif
1084
1085    catched = 0;
1086    zend_try {
1087        op_array = xc_entry_install(&xce, h TSRMLS_CC);
1088    } zend_catch {
1089        catched = 1;
1090    } zend_end_try();
1091
1092#define X_FREE(var) \
1093    if (xce.data.php->var) { \
1094        efree(xce.data.php->var); \
1095    }
1096    X_FREE(classinfos)
1097    X_FREE(funcinfos)
1098#ifdef HAVE_XCACHE_CONSTANT
1099    X_FREE(constinfos)
1100#endif
1101#undef X_FREE
1102    efree(xce.data.php);
1103
1104    if (catched) {
1105        zend_bailout();
1106    }
1107#ifdef DEBUG
1108    fprintf(stderr, "restored\n");
1109#endif
1110    return op_array;
1111}
1112/* }}} */
1113
1114/* gdb helper functions, but N/A for coredump */
1115int xc_is_rw(const void *p) /* {{{ */
1116{
1117    xc_shm_t *shm;
1118    int i;
1119    if (!xc_initized) {
1120        return 0;
1121    }
1122    for (i = 0; i < xc_php_hcache.size; i ++) {
1123        shm = xc_php_caches[i]->shm;
1124        if (shm->handlers->is_readwrite(shm, p)) {
1125            return 1;
1126        }
1127    }
1128    for (i = 0; i < xc_var_hcache.size; i ++) {
1129        shm = xc_var_caches[i]->shm;
1130        if (shm->handlers->is_readwrite(shm, p)) {
1131            return 1;
1132        }
1133    }
1134    return 0;
1135}
1136/* }}} */
1137int xc_is_ro(const void *p) /* {{{ */
1138{
1139    xc_shm_t *shm;
1140    int i;
1141    if (!xc_initized) {
1142        return 0;
1143    }
1144    for (i = 0; i < xc_php_hcache.size; i ++) {
1145        shm = xc_php_caches[i]->shm;
1146        if (shm->handlers->is_readonly(shm, p)) {
1147            return 1;
1148        }
1149    }
1150    for (i = 0; i < xc_var_hcache.size; i ++) {
1151        shm = xc_var_caches[i]->shm;
1152        if (shm->handlers->is_readonly(shm, p)) {
1153            return 1;
1154        }
1155    }
1156    return 0;
1157}
1158/* }}} */
1159int xc_is_shm(const void *p) /* {{{ */
1160{
1161    return xc_is_ro(p) || xc_is_rw(p);
1162}
1163/* }}} */
1164
1165/* module helper function */
1166static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
1167{
1168    typedef struct {
1169        const char *prefix;
1170        zend_uchar (*getsize)();
1171        const char *(*get)(zend_uchar i);
1172    } xc_meminfo_t;
1173    xc_meminfo_t nameinfos[] = {
1174        { "",        xc_get_op_type_count,   xc_get_op_type   },
1175        { "",        xc_get_data_type_count, xc_get_data_type },
1176        { "",        xc_get_opcode_count,    xc_get_opcode    },
1177        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
1178        { NULL, NULL, NULL }
1179    };
1180    xc_meminfo_t* p;
1181    zend_uchar i, count;
1182    char const_name[96];
1183    int const_name_len;
1184    int undefdone = 0;
1185
1186    for (p = nameinfos; p->getsize; p ++) {
1187        count = p->getsize();
1188        for (i = 0; i < count; i ++) {
1189            const char *name = p->get(i);
1190            if (!name) continue;
1191            if (strcmp(name, "UNDEF") == 0) {
1192                if (undefdone) continue;
1193                undefdone = 1;
1194            }
1195            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
1196            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1197        }
1198    }
1199
1200    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1201    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1202    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1203    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1204    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1205    return 0;
1206}
1207/* }}} */
1208static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
1209{
1210    int i;
1211    xc_cache_t *cache;
1212    xc_shm_t *shm;
1213
1214    if (!caches) {
1215        return NULL;
1216    }
1217    shm = NULL;
1218    for (i = 0; i < hcache->size; i ++) {
1219        cache = caches[i];
1220        if (cache) {
1221            if (cache->lck) {
1222                xc_lock_destroy(cache->lck);
1223            }
1224            /* do NOT free
1225            if (cache->entries) {
1226                cache->mem->handlers->free(cache->mem, cache->entries);
1227            }
1228            cache->mem->handlers->free(cache->mem, cache);
1229            */
1230            shm = cache->shm;
1231            shm->handlers->memdestroy(cache->mem);
1232        }
1233    }
1234    free(caches);
1235    return shm;
1236}
1237/* }}} */
1238static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
1239{
1240    xc_cache_t **caches = NULL, *cache;
1241    xc_mem_t *mem;
1242    time_t now = time(NULL);
1243    int i;
1244    xc_memsize_t memsize;
1245
1246    memsize = shmsize / hcache->size;
1247
1248    /* Don't let it break out of mem after ALIGNed
1249     * This is important for
1250     * Simply loop until it fit our need
1251     */
1252    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
1253        if (memsize < ALIGN(1)) {
1254            CHECK(NULL, "cache too small");
1255        }
1256        memsize --;
1257    }
1258
1259    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1260
1261    for (i = 0; i < hcache->size; i ++) {
1262        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
1263        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
1264        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
1265        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
1266
1267        cache->hcache  = hcache;
1268        cache->hentry  = hentry;
1269        cache->shm     = shm;
1270        cache->mem     = mem;
1271        cache->cacheid = i;
1272        cache->last_gc_deletes = now;
1273        cache->last_gc_expires = now;
1274        caches[i] = cache;
1275    }
1276    return caches;
1277
1278err:
1279    if (caches) {
1280        xc_cache_destroy(caches, hcache);
1281    }
1282    return NULL;
1283}
1284/* }}} */
1285static void xc_destroy() /* {{{ */
1286{
1287    xc_shm_t *shm = NULL;
1288
1289    if (origin_compile_file) {
1290        zend_compile_file = origin_compile_file;
1291        origin_compile_file = NULL;
1292    }
1293
1294    if (xc_php_caches) {
1295        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
1296        xc_php_caches = NULL;
1297    }
1298    if (xc_var_caches) {
1299        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1300        xc_var_caches = NULL;
1301    }
1302    if (shm) {
1303        xc_shm_destroy(shm);
1304    }
1305}
1306/* }}} */
1307static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1308{
1309    xc_shm_t *shm;
1310
1311    xc_php_caches = xc_var_caches = NULL;
1312
1313    if (xc_php_size || xc_var_size) {
1314        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");
1315        if (!shm->handlers->can_readonly(shm)) {
1316            xc_readonly_protection = 0;
1317        }
1318
1319        if (xc_php_size) {
1320            origin_compile_file = zend_compile_file;
1321            zend_compile_file = xc_compile_file;
1322
1323            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
1324        }
1325
1326        if (xc_var_size) {
1327            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
1328        }
1329    }
1330    return 1;
1331
1332err:
1333    if (xc_php_caches || xc_var_caches) {
1334        xc_destroy();
1335        /* shm destroied */
1336    }
1337    else if (shm) {
1338        xc_shm_destroy(shm);
1339    }
1340    return 0;
1341}
1342/* }}} */
1343static void xc_request_init(TSRMLS_D) /* {{{ */
1344{
1345    int i;
1346
1347    if (xc_php_hcache.size && !XG(php_holds)) {
1348        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1349        for (i = 0; i < xc_php_hcache.size; i ++) {
1350            xc_stack_init(&XG(php_holds[i]));
1351        }
1352    }
1353
1354    if (xc_var_hcache.size && !XG(var_holds)) {
1355        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1356        for (i = 0; i < xc_var_hcache.size; i ++) {
1357            xc_stack_init(&XG(var_holds[i]));
1358        }
1359    }
1360
1361    if (XG(cacher)) {
1362#if PHP_API_VERSION <= 20041225
1363        XG(request_time) = time(NULL);
1364#else
1365        XG(request_time) = sapi_get_request_time(TSRMLS_C);
1366#endif
1367    }
1368#ifdef HAVE_XCACHE_COVERAGER
1369    xc_coverager_request_init(TSRMLS_C);
1370#endif
1371}
1372/* }}} */
1373static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1374{
1375    xc_entry_unholds(TSRMLS_C);
1376    xc_gc_expires_php(TSRMLS_C);
1377    xc_gc_expires_var(TSRMLS_C);
1378    xc_gc_deletes(TSRMLS_C);
1379#ifdef HAVE_XCACHE_COVERAGER
1380    xc_coverager_request_shutdown(TSRMLS_C);
1381#endif
1382}
1383/* }}} */
1384/* {{{ PHP_GINIT_FUNCTION(xcache) */
1385static
1386#ifdef PHP_GINIT_FUNCTION
1387PHP_GINIT_FUNCTION(xcache)
1388#else
1389void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1390#endif
1391{
1392    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
1393}
1394/* }}} */
1395/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
1396static
1397#ifdef PHP_GSHUTDOWN_FUNCTION
1398PHP_GSHUTDOWN_FUNCTION(xcache)
1399#else
1400void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1401#endif
1402{
1403    int i;
1404
1405    if (xcache_globals->php_holds != NULL) {
1406        for (i = 0; i < xc_php_hcache.size; i ++) {
1407            xc_stack_destroy(&xcache_globals->php_holds[i]);
1408        }
1409        free(xcache_globals->php_holds);
1410        xcache_globals->php_holds = NULL;
1411    }
1412
1413    if (xcache_globals->var_holds != NULL) {
1414        for (i = 0; i < xc_var_hcache.size; i ++) {
1415            xc_stack_destroy(&xcache_globals->var_holds[i]);
1416        }
1417        free(xcache_globals->var_holds);
1418        xcache_globals->var_holds = NULL;
1419    }
1420}
1421/* }}} */
1422
1423/* user functions */
1424static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
1425{
1426    zval **server = NULL;
1427    zval **user = NULL;
1428    zval **pass = NULL;
1429    char *admin_user = NULL;
1430    char *admin_pass = NULL;
1431    HashTable *ht;
1432
1433    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1434        admin_user = NULL;
1435    }
1436    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1437        admin_pass = NULL;
1438    }
1439
1440    if (admin_user == NULL || admin_pass == NULL) {
1441        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1442        zend_bailout();
1443    }
1444    if (strlen(admin_pass) != 32) {
1445        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));
1446        zend_bailout();
1447    }
1448
1449#ifdef ZEND_ENGINE_2_1
1450    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
1451#endif
1452    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1453        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1454        zend_bailout();
1455    }
1456    ht = HASH_OF((*server));
1457
1458    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1459        user = NULL;
1460    }
1461    else if (Z_TYPE_PP(user) != IS_STRING) {
1462        user = NULL;
1463    }
1464
1465    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1466        pass = NULL;
1467    }
1468    else if (Z_TYPE_PP(pass) != IS_STRING) {
1469        pass = NULL;
1470    }
1471
1472    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1473        PHP_MD5_CTX context;
1474        char md5str[33];
1475        unsigned char digest[16];
1476
1477        PHP_MD5Init(&context);
1478        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
1479        PHP_MD5Final(digest, &context);
1480
1481        md5str[0] = '\0';
1482        make_digest(md5str, digest);
1483        if (strcmp(admin_pass, md5str) == 0) {
1484            return 1;
1485        }
1486    }
1487
1488#define STR "WWW-authenticate: basic realm='XCache Administration'"
1489    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1490#undef STR
1491#define STR "HTTP/1.0 401 Unauthorized"
1492    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1493#undef STR
1494    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1495
1496    zend_bailout();
1497    return 0;
1498}
1499/* }}} */
1500/* {{{ xcache_admin_operate */
1501typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
1502static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
1503{
1504    long type;
1505    int size;
1506    xc_cache_t **caches, *cache;
1507    long id = 0;
1508
1509    if (!xc_initized) {
1510        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache is not initized");
1511        RETURN_FALSE;
1512    }
1513
1514    xcache_admin_auth_check(TSRMLS_C);
1515
1516    if (optype == XC_OP_COUNT) {
1517        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1518            return;
1519        }
1520    }
1521    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1522        return;
1523    }
1524
1525    switch (type) {
1526        case XC_TYPE_PHP:
1527            size = xc_php_hcache.size;
1528            caches = xc_php_caches;
1529            break;
1530
1531        case XC_TYPE_VAR:
1532            size = xc_var_hcache.size;
1533            caches = xc_var_caches;
1534            break;
1535
1536        default:
1537            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1538            RETURN_FALSE;
1539    }
1540
1541    switch (optype) {
1542        case XC_OP_COUNT:
1543            RETURN_LONG(size)
1544            break;
1545
1546        case XC_OP_INFO:
1547        case XC_OP_LIST:
1548            if (id < 0 || id >= size) {
1549                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1550                RETURN_FALSE;
1551            }
1552
1553            array_init(return_value);
1554
1555            cache = caches[id];
1556            ENTER_LOCK(cache) {
1557                if (optype == XC_OP_INFO) {
1558                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
1559                }
1560                else {
1561                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1562                }
1563            } LEAVE_LOCK(cache);
1564            break;
1565        case XC_OP_CLEAR:
1566            {
1567                xc_entry_t *e, *next;
1568                int i, c;
1569
1570                if (id < 0 || id >= size) {
1571                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1572                    RETURN_FALSE;
1573                }
1574
1575                cache = caches[id];
1576                ENTER_LOCK(cache) {
1577                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
1578                        for (e = cache->entries[i]; e; e = next) {
1579                            next = e->next;
1580                            xc_entry_remove_dmz(e TSRMLS_CC);
1581                        }
1582                        cache->entries[i] = NULL;
1583                    }
1584                } LEAVE_LOCK(cache);
1585                xc_gc_deletes(TSRMLS_C);
1586            }
1587            break;
1588
1589        default:
1590            assert(0);
1591    }
1592}
1593/* }}} */
1594/* {{{ proto int xcache_count(int type)
1595   Return count of cache on specified cache type */
1596PHP_FUNCTION(xcache_count)
1597{
1598    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1599}
1600/* }}} */
1601/* {{{ proto array xcache_info(int type, int id)
1602   Get cache info by id on specified cache type */
1603PHP_FUNCTION(xcache_info)
1604{
1605    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1606}
1607/* }}} */
1608/* {{{ proto array xcache_list(int type, int id)
1609   Get cache entries list by id on specified cache type */
1610PHP_FUNCTION(xcache_list)
1611{
1612    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1613}
1614/* }}} */
1615/* {{{ proto array xcache_clear_cache(int type, int id)
1616   Clear cache by id on specified cache type */
1617PHP_FUNCTION(xcache_clear_cache)
1618{
1619    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1620}
1621/* }}} */
1622
1623static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1624{
1625    xc_hash_value_t hv;
1626    int cacheid;
1627
1628    switch (Z_TYPE_P(name)) {
1629#ifdef IS_UNICODE
1630        case IS_UNICODE:
1631#endif
1632        case IS_STRING:
1633            break;
1634        default:
1635#ifdef IS_UNICODE
1636            convert_to_text(name);
1637#else
1638            convert_to_string(name);
1639#endif
1640    }
1641#ifdef IS_UNICODE
1642    xce->name_type = name->type;
1643#endif
1644    xce->name = name->value;
1645
1646    hv = xc_entry_hash_var(xce TSRMLS_CC);
1647
1648    cacheid = (hv & xc_var_hcache.mask);
1649    xce->cache = xc_var_caches[cacheid];
1650    hv >>= xc_var_hcache.bits;
1651    xce->hvalue = (hv & xc_var_hentry.mask);
1652
1653    xce->type = XC_TYPE_VAR;
1654    return SUCCESS;
1655}
1656/* }}} */
1657/* {{{ proto mixed xcache_get(string name)
1658   Get cached data by specified name */
1659PHP_FUNCTION(xcache_get)
1660{
1661    xc_entry_t xce, *stored_xce;
1662    xc_entry_data_var_t var;
1663    zval *name;
1664
1665    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1666        return;
1667    }
1668    xce.data.var = &var;
1669    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1670
1671    ENTER_LOCK(xce.cache) {
1672        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1673        if (stored_xce) {
1674            if (XG(request_time) <= stored_xce->ctime + stored_xce->ttl) {
1675                xc_processor_restore_zval(return_value, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
1676                /* return */
1677                break;
1678            }
1679            else {
1680                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1681            }
1682        }
1683
1684        RETVAL_NULL();
1685    } LEAVE_LOCK(xce.cache);
1686}
1687/* }}} */
1688/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1689   Store data to cache by specified name */
1690PHP_FUNCTION(xcache_set)
1691{
1692    xc_entry_t xce, *stored_xce;
1693    xc_entry_data_var_t var;
1694    zval *name;
1695    zval *value;
1696
1697    xce.ttl = XG(var_ttl);
1698    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
1699        return;
1700    }
1701
1702    /* max ttl */
1703    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1704        xce.ttl = xc_var_maxttl;
1705    }
1706
1707    xce.data.var = &var;
1708    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1709
1710    ENTER_LOCK(xce.cache) {
1711        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1712        if (stored_xce) {
1713            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1714        }
1715        var.value = value;
1716        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
1717    } LEAVE_LOCK(xce.cache);
1718}
1719/* }}} */
1720/* {{{ proto bool  xcache_isset(string name)
1721   Check if an entry exists in cache by specified name */
1722PHP_FUNCTION(xcache_isset)
1723{
1724    xc_entry_t xce, *stored_xce;
1725    xc_entry_data_var_t var;
1726    zval *name;
1727
1728    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1729        return;
1730    }
1731    xce.data.var = &var;
1732    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1733
1734    ENTER_LOCK(xce.cache) {
1735        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1736        if (stored_xce) {
1737            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1738                RETVAL_TRUE;
1739                /* return */
1740                break;
1741            }
1742            else {
1743                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1744            }
1745        }
1746
1747        RETVAL_FALSE;
1748    } LEAVE_LOCK(xce.cache);
1749}
1750/* }}} */
1751/* {{{ proto bool  xcache_unset(string name)
1752   Unset existing data in cache by specified name */
1753PHP_FUNCTION(xcache_unset)
1754{
1755    xc_entry_t xce, *stored_xce;
1756    xc_entry_data_var_t var;
1757    zval *name;
1758
1759    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1760        return;
1761    }
1762    xce.data.var = &var;
1763    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1764
1765    ENTER_LOCK(xce.cache) {
1766        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1767        if (stored_xce) {
1768            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1769            RETVAL_TRUE;
1770        }
1771        else {
1772            RETVAL_FALSE;
1773        }
1774    } LEAVE_LOCK(xce.cache);
1775}
1776/* }}} */
1777static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1778{
1779    xc_entry_t xce, *stored_xce;
1780    xc_entry_data_var_t var, *stored_var;
1781    zval *name;
1782    long count = 1;
1783    long value = 0;
1784    zval oldzval;
1785
1786    xce.ttl = XG(var_ttl);
1787    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
1788        return;
1789    }
1790
1791    /* max ttl */
1792    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1793        xce.ttl = xc_var_maxttl;
1794    }
1795
1796    xce.data.var = &var;
1797    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1798
1799    ENTER_LOCK(xce.cache) {
1800        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1801        if (stored_xce) {
1802#ifdef DEBUG
1803            fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
1804#endif
1805            /* timeout */
1806            if (VAR_ENTRY_EXPIRED(stored_xce)) {
1807#ifdef DEBUG
1808                fprintf(stderr, "incdec: expired\n");
1809#endif
1810                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1811                stored_xce = NULL;
1812            }
1813            else {
1814                /* do it in place */
1815                stored_var = stored_xce->data.var;
1816                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
1817                    stored_xce->ctime = XG(request_time);
1818                    stored_xce->ttl   = xce.ttl;
1819#ifdef DEBUG
1820                    fprintf(stderr, "incdec: islong\n");
1821#endif
1822                    value = Z_LVAL_P(stored_var->value);
1823                    value += (inc == 1 ? count : - count);
1824                    RETVAL_LONG(value);
1825                    Z_LVAL_P(stored_var->value) = value;
1826                    break; /* leave lock */
1827                }
1828                else {
1829#ifdef DEBUG
1830                    fprintf(stderr, "incdec: notlong\n");
1831#endif
1832                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
1833                    convert_to_long(&oldzval);
1834                    value = Z_LVAL(oldzval);
1835                    zval_dtor(&oldzval);
1836                }
1837            }
1838        }
1839#ifdef DEBUG
1840        else {
1841            fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
1842        }
1843#endif
1844
1845        value += (inc == 1 ? count : - count);
1846        RETVAL_LONG(value);
1847        var.value = return_value;
1848
1849        if (stored_xce) {
1850            xce.atime = stored_xce->atime;
1851            xce.ctime = stored_xce->ctime;
1852            xce.hits  = stored_xce->hits;
1853            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1854        }
1855        xc_entry_store_dmz(&xce TSRMLS_CC);
1856
1857    } LEAVE_LOCK(xce.cache);
1858}
1859/* }}} */
1860/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
1861   Increase an int counter in cache by specified name, create it if not exists */
1862PHP_FUNCTION(xcache_inc)
1863{
1864    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1865}
1866/* }}} */
1867/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
1868   Decrease an int counter in cache by specified name, create it if not exists */
1869PHP_FUNCTION(xcache_dec)
1870{
1871    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1872}
1873/* }}} */
1874/* {{{ proto string xcache_asm(string filename)
1875 */
1876#ifdef HAVE_XCACHE_ASSEMBLER
1877PHP_FUNCTION(xcache_asm)
1878{
1879}
1880#endif
1881/* }}} */
1882#ifdef HAVE_XCACHE_DISASSEMBLER
1883/* {{{ proto array  xcache_dasm_file(string filename)
1884   Disassemble file into opcode array by filename */
1885PHP_FUNCTION(xcache_dasm_file)
1886{
1887    char *filename;
1888    int filename_len;
1889
1890    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
1891        return;
1892    }
1893    if (!filename_len) RETURN_FALSE;
1894
1895    xc_dasm_file(return_value, filename TSRMLS_CC);
1896}
1897/* }}} */
1898/* {{{ proto array  xcache_dasm_string(string code)
1899   Disassemble php code into opcode array */
1900PHP_FUNCTION(xcache_dasm_string)
1901{
1902    zval *code;
1903
1904    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
1905        return;
1906    }
1907    xc_dasm_string(return_value, code TSRMLS_CC);
1908}
1909/* }}} */
1910#endif
1911/* {{{ proto string xcache_encode(string filename)
1912   Encode php file into XCache opcode encoded format */
1913#ifdef HAVE_XCACHE_ENCODER
1914PHP_FUNCTION(xcache_encode)
1915{
1916}
1917#endif
1918/* }}} */
1919/* {{{ proto bool xcache_decode_file(string filename)
1920   Decode(load) opcode from XCache encoded format file */
1921#ifdef HAVE_XCACHE_DECODER
1922PHP_FUNCTION(xcache_decode_file)
1923{
1924}
1925#endif
1926/* }}} */
1927/* {{{ proto bool xcache_decode_string(string data)
1928   Decode(load) opcode from XCache encoded format data */
1929#ifdef HAVE_XCACHE_DECODER
1930PHP_FUNCTION(xcache_decode_string)
1931{
1932}
1933#endif
1934/* }}} */
1935/* {{{ xc_call_getter */
1936typedef const char *(xc_name_getter_t)(zend_uchar type);
1937static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
1938{
1939    long spec;
1940    const char *name;
1941
1942    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1943        return;
1944    }
1945    if (spec >= 0 && spec < count) {
1946        name = getter((zend_uchar) spec);
1947        if (name) {
1948            /* RETURN_STRING */
1949            int len = strlen(name);
1950            return_value->value.str.len = len;
1951            return_value->value.str.val = estrndup(name, len);
1952            return_value->type = IS_STRING; 
1953            return;
1954        }
1955    }
1956    RETURN_NULL();
1957}
1958/* }}} */
1959/* {{{ proto string xcache_get_op_type(int op_type) */
1960PHP_FUNCTION(xcache_get_op_type)
1961{
1962    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1963}
1964/* }}} */
1965/* {{{ proto string xcache_get_data_type(int type) */
1966PHP_FUNCTION(xcache_get_data_type)
1967{
1968    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1969}
1970/* }}} */
1971/* {{{ proto string xcache_get_opcode(int opcode) */
1972PHP_FUNCTION(xcache_get_opcode)
1973{
1974    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1975}
1976/* }}} */
1977/* {{{ proto string xcache_get_op_spec(int op_type) */
1978PHP_FUNCTION(xcache_get_op_spec)
1979{
1980    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1981}
1982/* }}} */
1983#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1984/* {{{ proto string xcache_get_opcode_spec(int opcode) */
1985PHP_FUNCTION(xcache_get_opcode_spec)
1986{
1987    long spec;
1988    const xc_opcode_spec_t *opspec;
1989
1990    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1991        return;
1992    }
1993    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
1994        opspec = xc_get_opcode_spec((zend_uchar) spec);
1995        if (opspec) {
1996            array_init(return_value);
1997            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
1998            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
1999            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
2000            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
2001            return;
2002        }
2003    }
2004    RETURN_NULL();
2005}
2006/* }}} */
2007#endif
2008/* {{{ proto mixed xcache_get_special_value(zval value) */
2009PHP_FUNCTION(xcache_get_special_value)
2010{
2011    zval *value;
2012
2013    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
2014        return;
2015    }
2016
2017    if (value->type == IS_CONSTANT) {
2018        *return_value = *value;
2019        zval_copy_ctor(return_value);
2020        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
2021        return;
2022    }
2023
2024    if (value->type == IS_CONSTANT_ARRAY) {
2025        *return_value = *value;
2026        zval_copy_ctor(return_value);
2027        return_value->type = IS_ARRAY;
2028        return;
2029    }
2030
2031    RETURN_NULL();
2032}
2033/* }}} */
2034/* {{{ proto string xcache_coredump(int op_type) */
2035PHP_FUNCTION(xcache_coredump)
2036{
2037    if (xc_test) {
2038        raise(SIGSEGV);
2039    }
2040    else {
2041        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
2042    }
2043}
2044/* }}} */
2045/* {{{ proto string xcache_is_autoglobal(string name) */
2046PHP_FUNCTION(xcache_is_autoglobal)
2047{
2048    char *name;
2049    int name_len;
2050
2051    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
2052        return;
2053    }
2054
2055    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
2056}
2057/* }}} */
2058static function_entry xcache_functions[] = /* {{{ */
2059{
2060    PHP_FE(xcache_count,             NULL)
2061    PHP_FE(xcache_info,              NULL)
2062    PHP_FE(xcache_list,              NULL)
2063    PHP_FE(xcache_clear_cache,       NULL)
2064    PHP_FE(xcache_coredump,          NULL)
2065#ifdef HAVE_XCACHE_ASSEMBLER
2066    PHP_FE(xcache_asm,               NULL)
2067#endif
2068#ifdef HAVE_XCACHE_DISASSEMBLER
2069    PHP_FE(xcache_dasm_file,         NULL)
2070    PHP_FE(xcache_dasm_string,       NULL)
2071#endif
2072#ifdef HAVE_XCACHE_ENCODER
2073    PHP_FE(xcache_encode,            NULL)
2074#endif
2075#ifdef HAVE_XCACHE_DECODER
2076    PHP_FE(xcache_decode_file,       NULL)
2077    PHP_FE(xcache_decode_string,     NULL)
2078#endif
2079#ifdef HAVE_XCACHE_COVERAGER
2080    PHP_FE(xcache_coverager_decode,  NULL)
2081    PHP_FE(xcache_coverager_start,   NULL)
2082    PHP_FE(xcache_coverager_stop,    NULL)
2083    PHP_FE(xcache_coverager_get,     NULL)
2084#endif
2085    PHP_FE(xcache_get_special_value, NULL)
2086    PHP_FE(xcache_get_op_type,       NULL)
2087    PHP_FE(xcache_get_data_type,     NULL)
2088    PHP_FE(xcache_get_opcode,        NULL)
2089#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
2090    PHP_FE(xcache_get_opcode_spec,   NULL)
2091#endif
2092    PHP_FE(xcache_is_autoglobal,     NULL)
2093    PHP_FE(xcache_inc,               NULL)
2094    PHP_FE(xcache_dec,               NULL)
2095    PHP_FE(xcache_get,               NULL)
2096    PHP_FE(xcache_set,               NULL)
2097    PHP_FE(xcache_isset,             NULL)
2098    PHP_FE(xcache_unset,             NULL)
2099    {NULL, NULL,                     NULL}
2100};
2101/* }}} */
2102
2103/* old signal handlers {{{ */
2104typedef void (*xc_sighandler_t)(int);
2105#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2106#include "foreachcoresig.h"
2107#undef FOREACH_SIG
2108/* }}} */
2109static void xcache_signal_handler(int sig);
2110static void xcache_restore_signal_handler() /* {{{ */
2111{
2112#define FOREACH_SIG(sig) do { \
2113    if (old_##sig##_handler != xcache_signal_handler) { \
2114        signal(sig, old_##sig##_handler); \
2115    } \
2116    else { \
2117        signal(sig, SIG_DFL); \
2118    } \
2119} while (0)
2120#include "foreachcoresig.h"
2121#undef FOREACH_SIG
2122}
2123/* }}} */
2124static void xcache_init_signal_handler() /* {{{ */
2125{
2126#define FOREACH_SIG(sig) \
2127    old_##sig##_handler = signal(sig, xcache_signal_handler)
2128#include "foreachcoresig.h"
2129#undef FOREACH_SIG
2130}
2131/* }}} */
2132static void xcache_signal_handler(int sig) /* {{{ */
2133{
2134    xcache_restore_signal_handler();
2135    if (xc_coredump_dir && xc_coredump_dir[0]) {
2136        chdir(xc_coredump_dir);
2137    }
2138    raise(sig);
2139}
2140/* }}} */
2141
2142/* {{{ PHP_INI */
2143
2144static PHP_INI_MH(xc_OnUpdateDummy)
2145{
2146    return SUCCESS;
2147}
2148
2149static PHP_INI_MH(xc_OnUpdateULong)
2150{
2151    zend_ulong *p = (zend_ulong *) mh_arg1;
2152
2153    *p = (zend_ulong) atoi(new_value);
2154    return SUCCESS;
2155}
2156
2157static PHP_INI_MH(xc_OnUpdateBool)
2158{
2159    zend_bool *p = (zend_bool *)mh_arg1;
2160
2161    if (strncasecmp("on", new_value, sizeof("on"))) {
2162        *p = (zend_bool) atoi(new_value);
2163    }
2164    else {
2165        *p = (zend_bool) 1;
2166    }
2167    return SUCCESS;
2168}
2169
2170static PHP_INI_MH(xc_OnUpdateString)
2171{
2172    char **p = (char**)mh_arg1;
2173    if (*p) {
2174        pefree(*p, 1);
2175    }
2176    *p = pemalloc(strlen(new_value) + 1, 1);
2177    strcpy(*p, new_value);
2178    return SUCCESS;
2179}
2180
2181#ifndef ZEND_ENGINE_2
2182#define OnUpdateLong OnUpdateInt
2183#endif
2184
2185#ifdef ZEND_WIN32
2186#   define DEFAULT_PATH "xcache"
2187#else
2188#   define DEFAULT_PATH "/dev/zero"
2189#endif
2190PHP_INI_BEGIN()
2191    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
2192    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2193    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2194    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
2195    /* opcode cache */
2196    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2197    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2198    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2199    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
2200    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
2201    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
2202    /* var cache */
2203    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2204    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2205    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2206    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
2207    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
2208
2209    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
2210    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
2211#ifdef HAVE_XCACHE_OPTIMIZER
2212    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2213#endif
2214    STD_PHP_INI_BOOLEAN("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
2215#ifdef HAVE_XCACHE_COVERAGER
2216    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
2217    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2218#endif
2219PHP_INI_END()
2220/* }}} */
2221/* {{{ PHP_MINFO_FUNCTION(xcache) */
2222static PHP_MINFO_FUNCTION(xcache)
2223{
2224    char buf[100];
2225    char *ptr;
2226    int left, len;
2227    xc_shm_scheme_t *scheme;
2228    char *covdumpdir;
2229
2230    php_info_print_table_start();
2231    php_info_print_table_header(2, "XCache Support", "enabled");
2232    php_info_print_table_row(2, "Version", XCACHE_VERSION);
2233    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
2234    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
2235
2236    if (xc_php_size) {
2237        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
2238        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
2239        php_info_print_table_row(2, "Opcode Cache", buf);
2240        efree(ptr);
2241    }
2242    else {
2243        php_info_print_table_row(2, "Opcode Cache", "disabled");
2244    }
2245    if (xc_var_size) {
2246        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
2247        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
2248        php_info_print_table_row(2, "Variable Cache", buf);
2249        efree(ptr);
2250    }
2251    else {
2252        php_info_print_table_row(2, "Variable Cache", "disabled");
2253    }
2254
2255    left = sizeof(buf);
2256    ptr = buf;
2257    buf[0] = '\0';
2258    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
2259        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
2260        left -= len;
2261        ptr += len;
2262    }
2263    php_info_print_table_row(2, "Shared Memory Schemes", buf);
2264
2265#ifdef HAVE_XCACHE_COVERAGER
2266    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
2267        covdumpdir = NULL;
2268    }
2269    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
2270#endif
2271    php_info_print_table_end();
2272
2273    DISPLAY_INI_ENTRIES();
2274}
2275/* }}} */
2276/* {{{ extension startup */
2277static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
2278{
2279    zend_extension extension;
2280
2281    extension = *new_extension;
2282    extension.handle = handle;
2283
2284    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
2285
2286    zend_llist_add_element(&zend_extensions, &extension);
2287#ifdef DEBUG
2288    fprintf(stderr, "registered\n");
2289#endif
2290}
2291
2292/* dirty check */
2293#if defined(COMPILE_DL_XCACHE) && (defined(ZEND_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__))
2294#   define zend_append_version_info(x) do { } while (0)
2295#else
2296extern void zend_append_version_info(zend_extension *extension);
2297#endif
2298static int xc_zend_extension_startup(zend_extension *extension)
2299{
2300    if (extension->startup) {
2301        if (extension->startup(extension) != SUCCESS) {
2302            return FAILURE;
2303        }
2304        zend_append_version_info(extension);
2305    }
2306    return SUCCESS;
2307}
2308/* }}} */
2309static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
2310{
2311    int bits, size;
2312    char *value;
2313
2314    if (cfg_get_string(name, &value) != SUCCESS) {
2315        value = default_value;
2316    }
2317
2318    p->size = zend_atoi(value, strlen(value));
2319    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
2320        /* empty body */
2321    }
2322    p->size = size;
2323    p->bits = bits;
2324    p->mask = size - 1;
2325
2326    return SUCCESS;
2327}
2328/* }}} */
2329static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
2330{
2331    char *value;
2332
2333    if (cfg_get_string(name, &value) != SUCCESS) {
2334        value = default_value;
2335    }
2336
2337    *p = zend_atoi(value, strlen(value));
2338    return SUCCESS;
2339}
2340/* }}} */
2341/* {{{ PHP_MINIT_FUNCTION(xcache) */
2342static PHP_MINIT_FUNCTION(xcache)
2343{
2344    char *env;
2345    zend_extension *ext;
2346    zend_llist_position lpos;
2347
2348    xc_module_gotup = 1;
2349    if (!xc_zend_extension_gotup) {
2350        if (zend_get_extension(XCACHE_NAME) == NULL) {
2351            xc_zend_extension_register(&zend_extension_entry, 0);
2352            xc_zend_extension_startup(&zend_extension_entry);
2353        }
2354    }
2355
2356    ext = zend_get_extension("Zend Optimizer");
2357    if (ext) {
2358        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
2359        ext->op_array_handler = NULL;
2360    }
2361    /* cache if there's an op_array_ctor */
2362    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
2363            ext;
2364            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
2365        if (ext->op_array_ctor) {
2366            xc_have_op_array_ctor = 1;
2367            break;
2368        }
2369    }
2370
2371
2372#ifndef PHP_GINIT
2373    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
2374#endif
2375    REGISTER_INI_ENTRIES();
2376
2377    if (strcmp(sapi_module.name, "cli") == 0) {
2378        if ((env = getenv("XCACHE_TEST")) != NULL) {
2379            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2380        }
2381        if (!xc_test) {
2382            /* disable cache for cli except for test */
2383            xc_php_size = xc_var_size = 0;
2384        }
2385    }
2386
2387    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2388    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2389    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
2390
2391    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
2392    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
2393    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
2394
2395    if (xc_php_size <= 0) {
2396        xc_php_size = xc_php_hcache.size = 0;
2397    }
2398    if (xc_var_size <= 0) {
2399        xc_var_size = xc_var_hcache.size = 0;
2400    }
2401
2402    if (xc_coredump_dir && xc_coredump_dir[0]) {
2403        xcache_init_signal_handler();
2404    }
2405
2406    xc_init_constant(module_number TSRMLS_CC);
2407    xc_shm_init_modules();
2408
2409    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
2410        if (!xc_init(module_number TSRMLS_CC)) {
2411            zend_error(E_ERROR, "XCache: Cannot init");
2412            goto err_init;
2413        }
2414        xc_initized = 1;
2415    }
2416
2417#ifdef HAVE_XCACHE_COVERAGER
2418    xc_coverager_init(module_number TSRMLS_CC);
2419#endif
2420
2421    return SUCCESS;
2422
2423err_init:
2424    return FAILURE;
2425}
2426/* }}} */
2427/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2428static PHP_MSHUTDOWN_FUNCTION(xcache)
2429{
2430    if (xc_initized) {
2431        xc_destroy();
2432        xc_initized = 0;
2433    }
2434    if (xc_mmap_path) {
2435        pefree(xc_mmap_path, 1);
2436        xc_mmap_path = NULL;
2437    }
2438    if (xc_shm_scheme) {
2439        pefree(xc_shm_scheme, 1);
2440        xc_shm_scheme = NULL;
2441    }
2442
2443#ifdef HAVE_XCACHE_COVERAGER
2444    xc_coverager_destroy();
2445#endif
2446
2447    if (xc_coredump_dir && xc_coredump_dir[0]) {
2448        xcache_restore_signal_handler();
2449    }
2450    if (xc_coredump_dir) {
2451        pefree(xc_coredump_dir, 1);
2452        xc_coredump_dir = NULL;
2453    }
2454#ifndef PHP_GINIT
2455#   ifdef ZTS
2456    ts_free_id(xcache_globals_id);
2457#   else
2458    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
2459#   endif
2460#endif
2461
2462    UNREGISTER_INI_ENTRIES();
2463    return SUCCESS;
2464}
2465/* }}} */
2466/* {{{ PHP_RINIT_FUNCTION(xcache) */
2467static PHP_RINIT_FUNCTION(xcache)
2468{
2469    xc_request_init(TSRMLS_C);
2470    return SUCCESS;
2471}
2472/* }}} */
2473/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2474#ifndef ZEND_ENGINE_2
2475static PHP_RSHUTDOWN_FUNCTION(xcache)
2476#else
2477static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2478#endif
2479{
2480#ifdef ZEND_ENGINE_2
2481    TSRMLS_FETCH();
2482#endif
2483
2484    xc_request_shutdown(TSRMLS_C);
2485    return SUCCESS;
2486}
2487/* }}} */
2488/* {{{ module definition structure */
2489
2490zend_module_entry xcache_module_entry = {
2491    STANDARD_MODULE_HEADER,
2492    "XCache",
2493    xcache_functions,
2494    PHP_MINIT(xcache),
2495    PHP_MSHUTDOWN(xcache),
2496    PHP_RINIT(xcache),
2497#ifndef ZEND_ENGINE_2
2498    PHP_RSHUTDOWN(xcache),
2499#else
2500    NULL,
2501#endif
2502    PHP_MINFO(xcache),
2503    XCACHE_VERSION,
2504#ifdef PHP_GINIT
2505    PHP_MODULE_GLOBALS(xcache),
2506    PHP_GINIT(xcache),
2507    PHP_GSHUTDOWN(xcache),
2508#endif
2509#ifdef ZEND_ENGINE_2
2510    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2511#else
2512    NULL,
2513    NULL,
2514#endif
2515    STANDARD_MODULE_PROPERTIES_EX
2516};
2517
2518#ifdef COMPILE_DL_XCACHE
2519ZEND_GET_MODULE(xcache)
2520#endif
2521/* }}} */
2522static startup_func_t xc_last_ext_startup;
2523static zend_llist_element *xc_llist_element;
2524static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
2525{
2526    return p1 == p2;
2527}
2528/* }}} */
2529static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
2530{
2531    /* restore */
2532    extension->startup = xc_last_ext_startup;
2533    if (extension->startup) {
2534        if (extension->startup(extension) != SUCCESS) {
2535            return FAILURE;
2536        }
2537    }
2538    xc_zend_extension_register(&zend_extension_entry, 0);
2539    if (!xc_module_gotup) {
2540        return zend_startup_module(&xcache_module_entry);
2541    }
2542    return SUCCESS;
2543}
2544/* }}} */
2545ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2546{
2547    if (xc_zend_extension_gotup) {
2548        return FAILURE;
2549    }
2550    xc_zend_extension_gotup = 1;
2551    xc_llist_element = NULL;
2552    if (zend_llist_count(&zend_extensions) > 1) {
2553        zend_llist_position lpos;
2554        zend_extension *ext;
2555        llist_dtor_func_t dtor;
2556
2557        ext = zend_get_extension(XCACHE_NAME);
2558        assert(ext);
2559        dtor = zend_extensions.dtor; /* avoid dtor */
2560        zend_extensions.dtor = NULL;
2561        zend_llist_del_element(&zend_extensions, ext, xc_ptr_compare_func);
2562        zend_extensions.dtor = dtor;
2563
2564        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
2565        assert(ext);
2566        xc_last_ext_startup = ext->startup;
2567        ext->startup = xc_zend_startup_last;
2568    }
2569    else if (!xc_module_gotup) {
2570        return zend_startup_module(&xcache_module_entry);
2571    }
2572    return SUCCESS;
2573}
2574/* }}} */
2575ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2576{
2577    /* empty */
2578}
2579/* }}} */
2580ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2581{
2582#ifdef HAVE_XCACHE_COVERAGER
2583    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
2584#endif
2585}
2586/* }}} */
2587ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2588{
2589#if 0
2590    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
2591#endif
2592}
2593/* }}} */
2594ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2595{
2596#if 0
2597    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
2598#endif
2599}
2600/* }}} */
2601/* {{{ zend extension definition structure */
2602ZEND_DLEXPORT zend_extension zend_extension_entry = {
2603    XCACHE_NAME,
2604    XCACHE_VERSION,
2605    XCACHE_AUTHOR,
2606    XCACHE_URL,
2607    XCACHE_COPYRIGHT,
2608    xcache_zend_startup,
2609    xcache_zend_shutdown,
2610    NULL,           /* activate_func_t */
2611    NULL,           /* deactivate_func_t */
2612    NULL,           /* message_handler_func_t */
2613    NULL,           /* op_array_handler_func_t */
2614    xcache_statement_handler,
2615    xcache_fcall_begin_handler,
2616    xcache_fcall_end_handler,
2617    NULL,           /* op_array_ctor_func_t */
2618    NULL,           /* op_array_dtor_func_t */
2619    STANDARD_ZEND_EXTENSION_PROPERTIES
2620};
2621
2622#ifndef ZEND_EXT_API
2623#   define ZEND_EXT_API ZEND_DLEXPORT
2624#endif
2625#if COMPILE_DL_XCACHE
2626ZEND_EXTENSION();
2627#endif
2628/* }}} */
Note: See TracBrowser for help on using the repository browser.