source: trunk/mod_cacher/xc_cacher.c @ 1065

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

refactor: sign clean up

  • Property svn:eol-style set to native
File size: 99.7 KB
Line 
1#if 0
2#define XCACHE_DEBUG
3#endif
4
5#if 0
6#define SHOW_DPRINT
7#endif
8
9/* {{{ macros */
10#include "xc_cacher.h"
11#include "xc_cache.h"
12#include "xcache.h"
13#include "xc_processor.h"
14#include "xcache_globals.h"
15#include "xcache/xc_extension.h"
16#include "xcache/xc_ini.h"
17#include "xcache/xc_utils.h"
18#include "xcache/xc_sandbox.h"
19#include "util/xc_trace.h"
20#include "util/xc_vector.h"
21#include "util/xc_align.h"
22
23#include "php.h"
24#include "ext/standard/info.h"
25#include "ext/standard/md5.h"
26#ifdef ZEND_ENGINE_2_1
27#include "ext/date/php_date.h"
28#endif
29#include "ext/standard/php_math.h"
30#include "SAPI.h"
31
32#define ECALLOC_N(x, n) ((x) = ecalloc(n, sizeof((x)[0])))
33#define ECALLOC_ONE(x) ECALLOC_N(x, 1)
34#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > (pentry)->ctime + (time_t) (pentry)->ttl)
35#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
36#define LOCK(x) xc_lock((x)->lck)
37#define UNLOCK(x) xc_unlock((x)->lck)
38
39#define ENTER_LOCK_EX(x) \
40    xc_lock((x)->lck); \
41    zend_try { \
42        do
43#define LEAVE_LOCK_EX(x) \
44        while (0); \
45    } zend_catch { \
46        catched = 1; \
47    } zend_end_try(); \
48    xc_unlock((x)->lck)
49
50#define ENTER_LOCK(x) do { \
51    int catched = 0; \
52    ENTER_LOCK_EX(x)
53#define LEAVE_LOCK(x) \
54    LEAVE_LOCK_EX(x); \
55    if (catched) { \
56        zend_bailout(); \
57    } \
58} while(0)
59/* }}} */
60
61struct _xc_hash_t { /* {{{ */
62    size_t bits;
63    size_t size;
64    xc_hash_value_t mask;
65};
66/* }}} */
67struct _xc_cached_t { /* {{{ stored in shm */
68    int cacheid;
69
70    time_t     compiling;
71    time_t     disabled;
72    zend_ulong updates;
73    zend_ulong hits;
74    zend_ulong skips;
75    zend_ulong ooms;
76    zend_ulong errors;
77
78    xc_entry_t **entries;
79    int entries_count;
80    xc_entry_data_php_t **phps;
81    int phps_count;
82    xc_entry_t *deletes;
83    int deletes_count;
84
85    time_t     last_gc_deletes;
86    time_t     last_gc_expires;
87
88    time_t     hits_by_hour_cur_time;
89    zend_uint  hits_by_hour_cur_slot;
90    zend_ulong hits_by_hour[24];
91    time_t     hits_by_second_cur_time;
92    zend_uint  hits_by_second_cur_slot;
93    zend_ulong hits_by_second[5];
94};
95/* }}} */
96
97/* {{{ globals */
98static char *xc_shm_scheme = NULL;
99static char *xc_mmap_path = NULL;
100
101static zend_bool xc_admin_enable_auth = 1;
102static xc_hash_t xc_php_hcache = { 0, 0, 0 };
103static xc_hash_t xc_php_hentry = { 0, 0, 0 };
104static xc_hash_t xc_var_hcache = { 0, 0, 0 };
105static xc_hash_t xc_var_hentry = { 0, 0, 0 };
106
107static zend_ulong xc_php_ttl    = 0;
108static zend_ulong xc_var_maxttl = 0;
109
110enum { xc_deletes_gc_interval = 120 };
111static zend_ulong xc_php_gc_interval = 0;
112static zend_ulong xc_var_gc_interval = 0;
113
114/* total size */
115static zend_ulong xc_php_size  = 0;
116static zend_ulong xc_var_size  = 0;
117
118static xc_cache_t *xc_php_caches = NULL;
119static xc_cache_t *xc_var_caches = NULL;
120
121static zend_bool xc_initized = 0;
122static time_t xc_init_time = 0;
123static long unsigned xc_init_instance_id = 0;
124#ifdef ZTS
125static long unsigned xc_init_instance_subid = 0;
126#endif
127static zend_compile_file_t *old_compile_file = NULL;
128
129static zend_bool xc_readonly_protection = 0;
130
131zend_bool xc_have_op_array_ctor = 0;
132
133typedef enum { XC_TYPE_PHP, XC_TYPE_VAR } xc_entry_type_t;
134
135/* any function in *_unlocked is only safe be called within locked (single thread access) area */
136
137static void xc_php_add_unlocked(xc_cached_t *cached, xc_entry_data_php_t *php) /* {{{ */
138{
139    xc_entry_data_php_t **head = &(cached->phps[php->hvalue]);
140    php->next = *head;
141    *head = php;
142    cached->phps_count ++;
143}
144/* }}} */
145static xc_entry_data_php_t *xc_php_store_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
146{
147    xc_entry_data_php_t *stored_php;
148
149    php->hits     = 0;
150    php->refcount = 0;
151    stored_php = xc_processor_store_xc_entry_data_php_t(cache, php TSRMLS_CC);
152    if (stored_php) {
153        xc_php_add_unlocked(cache->cached, stored_php);
154        return stored_php;
155    }
156    else {
157        cache->cached->ooms ++;
158        return NULL;
159    }
160}
161/* }}} */
162static xc_entry_data_php_t *xc_php_find_unlocked(xc_cached_t *cached, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
163{
164    xc_entry_data_php_t *p;
165    for (p = cached->phps[php->hvalue]; p; p = (xc_entry_data_php_t *) p->next) {
166        if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
167            p->hits ++;
168            return p;
169        }
170    }
171    return NULL;
172}
173/* }}} */
174static void xc_php_free_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
175{
176    cache->mem->handlers->free(cache->mem, (xc_entry_data_php_t *)php);
177}
178/* }}} */
179static void xc_php_addref_unlocked(xc_entry_data_php_t *php) /* {{{ */
180{
181    php->refcount ++;
182}
183/* }}} */
184static void xc_php_release_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
185{
186    if (-- php->refcount == 0) {
187        xc_entry_data_php_t **pp = &(cache->cached->phps[php->hvalue]);
188        xc_entry_data_php_t *p;
189        for (p = *pp; p; pp = &(p->next), p = p->next) {
190            if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
191                /* unlink */
192                *pp = p->next;
193                xc_php_free_unlocked(cache, php);
194                return;
195            }
196        }
197        assert(0);
198    }
199}
200/* }}} */
201
202static inline int xc_entry_equal_unlocked(xc_entry_type_t type, const xc_entry_t *entry1, const xc_entry_t *entry2 TSRMLS_DC) /* {{{ */
203{
204    /* this function isn't required but can be in unlocked */
205    switch (type) {
206        case XC_TYPE_PHP:
207            {
208                const xc_entry_php_t *php_entry1 = (const xc_entry_php_t *) entry1;
209                const xc_entry_php_t *php_entry2 = (const xc_entry_php_t *) entry2;
210                if (php_entry1->file_inode && php_entry2->file_inode) {
211                    zend_bool inodeIsSame = php_entry1->file_inode == php_entry2->file_inode
212                                         && php_entry1->file_device == php_entry2->file_device;
213                    if (XG(experimental)) {
214                        /* new experimental behavior: quick check by inode, first */
215                        if (!inodeIsSame) {
216                            return 0;
217                        }
218
219                        /* and then opened_path compare */
220                    }
221                    else {
222                        /* old behavior: inode check only */
223                        return inodeIsSame;
224                    }
225                }
226            }
227
228            assert(IS_ABSOLUTE_PATH(entry1->name.str.val, entry1->name.str.len));
229            assert(IS_ABSOLUTE_PATH(entry2->name.str.val, entry2->name.str.len));
230
231            return entry1->name.str.len == entry2->name.str.len
232                && memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
233
234        case XC_TYPE_VAR:
235#ifdef IS_UNICODE
236            if (entry1->name_type != entry2->name_type) {
237                return 0;
238            }
239
240            if (entry1->name_type == IS_UNICODE) {
241                return entry1->name.ustr.len == entry2->name.ustr.len
242                    && memcmp(entry1->name.ustr.val, entry2->name.ustr.val, (entry1->name.ustr.len + 1) * sizeof(UChar)) == 0;
243            }
244#endif
245            return entry1->name.str.len == entry2->name.str.len
246                && memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
247            break;
248
249        default:
250            assert(0);
251    }
252    return 0;
253}
254/* }}} */
255static inline int xc_entry_has_prefix_unlocked(xc_entry_type_t type, xc_entry_t *entry, zval *prefix) /* {{{ */
256{
257    /* this function isn't required but can be in unlocked */
258
259#ifdef IS_UNICODE
260    if (entry->name_type != prefix->type) {
261        return 0;
262    }
263
264    if (entry->name_type == IS_UNICODE) {
265        if (entry->name.ustr.len < Z_USTRLEN_P(prefix)) {
266            return 0;
267        }
268        return memcmp(entry->name.ustr.val, Z_USTRVAL_P(prefix), Z_USTRLEN_P(prefix) * sizeof(UChar)) == 0;
269    }
270#endif
271    if (prefix->type != IS_STRING) {
272        return 0;
273    }
274
275    if (entry->name.str.len < Z_STRLEN_P(prefix)) {
276        return 0;
277    }
278
279    return memcmp(entry->name.str.val, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)) == 0;
280}
281/* }}} */
282static void xc_entry_add_unlocked(xc_cached_t *cached, xc_hash_value_t entryslotid, xc_entry_t *entry) /* {{{ */
283{
284    xc_entry_t **head = &(cached->entries[entryslotid]);
285    entry->next = *head;
286    *head = entry;
287    cached->entries_count ++;
288}
289/* }}} */
290static xc_entry_t *xc_entry_store_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
291{
292    xc_entry_t *stored_entry;
293
294    entry->hits  = 0;
295    entry->ctime = XG(request_time);
296    entry->atime = XG(request_time);
297    stored_entry = type == XC_TYPE_PHP
298        ? (xc_entry_t *) xc_processor_store_xc_entry_php_t(cache, (xc_entry_php_t *) entry TSRMLS_CC)
299        : (xc_entry_t *) xc_processor_store_xc_entry_var_t(cache, (xc_entry_var_t *) entry TSRMLS_CC);
300    if (stored_entry) {
301        xc_entry_add_unlocked(cache->cached, entryslotid, stored_entry);
302        ++cache->cached->updates;
303        return stored_entry;
304    }
305    else {
306        cache->cached->ooms ++;
307        return NULL;
308    }
309}
310/* }}} */
311static xc_entry_php_t *xc_entry_php_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
312{
313    return (xc_entry_php_t *) xc_entry_store_unlocked(XC_TYPE_PHP, cache, entryslotid, (xc_entry_t *) entry_php TSRMLS_CC);
314}
315/* }}} */
316static xc_entry_var_t *xc_entry_var_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_var_t *entry_var TSRMLS_DC) /* {{{ */
317{
318    return (xc_entry_var_t *) xc_entry_store_unlocked(XC_TYPE_VAR, cache, entryslotid, (xc_entry_t *) entry_var TSRMLS_CC);
319}
320/* }}} */
321static void xc_entry_free_real_unlocked(xc_entry_type_t type, xc_cache_t *cache, volatile xc_entry_t *entry) /* {{{ */
322{
323    if (type == XC_TYPE_PHP) {
324        xc_php_release_unlocked(cache, ((xc_entry_php_t *) entry)->php);
325    }
326    cache->mem->handlers->free(cache->mem, (xc_entry_t *)entry);
327}
328/* }}} */
329static void xc_entry_free_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_entry_t *entry TSRMLS_DC) /* {{{ */
330{
331    cache->cached->entries_count --;
332    if ((type == XC_TYPE_PHP ? ((xc_entry_php_t *) entry)->refcount : 0) == 0) {
333        xc_entry_free_real_unlocked(type, cache, entry);
334    }
335    else {
336        entry->next = cache->cached->deletes;
337        cache->cached->deletes = entry;
338        entry->dtime = XG(request_time);
339        cache->cached->deletes_count ++;
340    }
341    return;
342}
343/* }}} */
344static void xc_entry_remove_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
345{
346    xc_entry_t **pp = &(cache->cached->entries[entryslotid]);
347    xc_entry_t *p;
348    for (p = *pp; p; pp = &(p->next), p = p->next) {
349        if (xc_entry_equal_unlocked(type, entry, p TSRMLS_CC)) {
350            /* unlink */
351            *pp = p->next;
352            xc_entry_free_unlocked(type, cache, entry TSRMLS_CC);
353            return;
354        }
355    }
356    assert(0);
357}
358/* }}} */
359static xc_entry_t *xc_entry_find_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
360{
361    xc_entry_t *p;
362    for (p = cache->cached->entries[entryslotid]; p; p = p->next) {
363        if (xc_entry_equal_unlocked(type, entry, p TSRMLS_CC)) {
364            zend_bool fresh;
365            switch (type) {
366            case XC_TYPE_PHP:
367                {
368                    xc_entry_php_t *p_php = (xc_entry_php_t *) p;
369                    xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
370                    fresh = p_php->file_mtime == entry_php->file_mtime && p_php->file_size == entry_php->file_size;
371                }
372                break;
373
374            case XC_TYPE_VAR:
375                {
376                    fresh = !VAR_ENTRY_EXPIRED(p);
377                }
378                break;
379
380            default:
381                assert(0);
382            }
383
384            if (fresh) {
385                p->hits ++;
386                p->atime = XG(request_time);
387                return p;
388            }
389
390            xc_entry_remove_unlocked(type, cache, entryslotid, p TSRMLS_CC);
391            return NULL;
392        }
393    }
394    return NULL;
395}
396/* }}} */
397static void xc_entry_hold_php_unlocked(xc_cache_t *cache, xc_entry_php_t *entry TSRMLS_DC) /* {{{ */
398{
399    TRACE("hold %d:%s", entry->file_inode, entry->entry.name.str.val);
400    entry->refcount ++;
401    xc_stack_push(&XG(php_holds)[cache->cacheid], (void *)entry);
402}
403/* }}} */
404static inline zend_uint advance_wrapped(zend_uint val, zend_uint count) /* {{{ */
405{
406    if (val + 1 >= count) {
407        return 0;
408    }
409    return val + 1;
410}
411/* }}} */
412static inline void xc_counters_inc(time_t *curtime, zend_uint *curslot, time_t interval, zend_ulong *counters, zend_uint count TSRMLS_DC) /* {{{ */
413{
414    time_t n = XG(request_time) / interval;
415    if (*curtime != n) {
416        zend_uint target_slot = n % count;
417        zend_uint slot;
418        for (slot = advance_wrapped(*curslot, count);
419                slot != target_slot;
420                slot = advance_wrapped(slot, count)) {
421            counters[slot] = 0;
422        }
423        counters[target_slot] = 0;
424        *curtime = n;
425        *curslot = target_slot;
426    }
427    counters[*curslot] ++;
428}
429/* }}} */
430static inline void xc_cached_hit_unlocked(xc_cached_t *cached TSRMLS_DC) /* {{{ */
431{
432    cached->hits ++;
433
434    xc_counters_inc(&cached->hits_by_hour_cur_time
435            , &cached->hits_by_hour_cur_slot, 60 * 60
436            , cached->hits_by_hour
437            , sizeof(cached->hits_by_hour) / sizeof(cached->hits_by_hour[0])
438            TSRMLS_CC);
439
440    xc_counters_inc(&cached->hits_by_second_cur_time
441            , &cached->hits_by_second_cur_slot, 1
442            , cached->hits_by_second
443            , sizeof(cached->hits_by_second) / sizeof(cached->hits_by_second[0])
444            TSRMLS_CC);
445}
446/* }}} */
447
448/* helper function that loop through each entry */
449#define XC_ENTRY_APPLY_FUNC(name) zend_bool name(xc_entry_t *entry TSRMLS_DC)
450typedef XC_ENTRY_APPLY_FUNC((*cache_apply_unlocked_func_t));
451static void xc_entry_apply_unlocked(xc_entry_type_t type, xc_cache_t *cache, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
452{
453    xc_entry_t *p, **pp;
454    int i, c;
455
456    for (i = 0, c = cache->hentry->size; i < c; i ++) {
457        pp = &(cache->cached->entries[i]);
458        for (p = *pp; p; p = *pp) {
459            if (apply_func(p TSRMLS_CC)) {
460                /* unlink */
461                *pp = p->next;
462                xc_entry_free_unlocked(type, cache, p TSRMLS_CC);
463            }
464            else {
465                pp = &(p->next);
466            }
467        }
468    }
469}
470/* }}} */
471
472#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
473/* call graph:
474 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_php_entry_unlocked
475 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_var_entry_unlocked
476 */
477static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_unlocked) /* {{{ */
478{
479    TRACE("ttl %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
480    if (XG(request_time) > entry->atime + (time_t) xc_php_ttl) {
481        return 1;
482    }
483    return 0;
484}
485/* }}} */
486static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_unlocked) /* {{{ */
487{
488    if (VAR_ENTRY_EXPIRED(entry)) {
489        return 1;
490    }
491    return 0;
492}
493/* }}} */
494static void xc_gc_expires_one(xc_entry_type_t type, xc_cache_t *cache, zend_ulong gc_interval, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
495{
496    TRACE("interval %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) cache->cached->last_gc_expires, gc_interval);
497    if (!cache->cached->disabled && XG(request_time) >= cache->cached->last_gc_expires + (time_t) gc_interval) {
498        ENTER_LOCK(cache) {
499            if (XG(request_time) >= cache->cached->last_gc_expires + (time_t) gc_interval) {
500                cache->cached->last_gc_expires = XG(request_time);
501                xc_entry_apply_unlocked(type, cache, apply_func TSRMLS_CC);
502            }
503        } LEAVE_LOCK(cache);
504    }
505}
506/* }}} */
507static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
508{
509    size_t i, c;
510
511    if (!xc_php_ttl || !xc_php_gc_interval || !xc_php_caches) {
512        return;
513    }
514
515    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
516        xc_gc_expires_one(XC_TYPE_PHP, &xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_unlocked TSRMLS_CC);
517    }
518}
519/* }}} */
520static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
521{
522    int i, c;
523
524    if (!xc_var_gc_interval || !xc_var_caches) {
525        return;
526    }
527
528    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
529        xc_gc_expires_one(XC_TYPE_VAR, &xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_unlocked TSRMLS_CC);
530    }
531}
532/* }}} */
533
534static XC_CACHE_APPLY_FUNC(xc_gc_delete_unlocked) /* {{{ */
535{
536    xc_entry_t *p, **pp;
537
538    pp = &cache->cached->deletes;
539    for (p = *pp; p; p = *pp) {
540        xc_entry_php_t *entry = (xc_entry_php_t *) p;
541        if (XG(request_time) - p->dtime > 3600) {
542            entry->refcount = 0;
543            /* issue warning here */
544        }
545        if (entry->refcount == 0) {
546            /* unlink */
547            *pp = p->next;
548            cache->cached->deletes_count --;
549            xc_entry_free_real_unlocked(XC_TYPE_PHP, cache, p);
550        }
551        else {
552            pp = &(p->next);
553        }
554    }
555}
556/* }}} */
557static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
558{
559    if (!cache->cached->disabled && cache->cached->deletes && XG(request_time) - cache->cached->last_gc_deletes > xc_deletes_gc_interval) {
560        ENTER_LOCK(cache) {
561            if (cache->cached->deletes && XG(request_time) - cache->cached->last_gc_deletes > xc_deletes_gc_interval) {
562                cache->cached->last_gc_deletes = XG(request_time);
563                xc_gc_delete_unlocked(cache TSRMLS_CC);
564            }
565        } LEAVE_LOCK(cache);
566    }
567}
568/* }}} */
569static void xc_gc_deletes(TSRMLS_D) /* {{{ */
570{
571    size_t i, c;
572
573    if (xc_php_caches) {
574        for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
575            xc_gc_deletes_one(&xc_php_caches[i] TSRMLS_CC);
576        }
577    }
578
579    if (xc_var_caches) {
580        for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
581            xc_gc_deletes_one(&xc_var_caches[i] TSRMLS_CC);
582        }
583    }
584}
585/* }}} */
586
587/* helper functions for user functions */
588static void xc_fillinfo_unlocked(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
589{
590    zval *blocks, *hits;
591    size_t i;
592    const xc_block_t *b;
593#ifndef NDEBUG
594    xc_memsize_t avail = 0;
595#endif
596    const xc_mem_t *mem = cache->mem;
597    const xc_mem_handlers_t *handlers = mem->handlers;
598    zend_ulong interval;
599    const xc_cached_t *cached = cache->cached;
600
601    if (cachetype == XC_TYPE_PHP) {
602        interval = xc_php_ttl ? xc_php_gc_interval : 0;
603    }
604    else {
605        interval = xc_var_gc_interval;
606    }
607
608    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
609    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cached->compiling);
610    add_assoc_long_ex(return_value, ZEND_STRS("disabled"),  cached->disabled);
611    add_assoc_long_ex(return_value, ZEND_STRS("updates"),   cached->updates);
612    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cached->updates); /* deprecated */
613    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cached->hits);
614    add_assoc_long_ex(return_value, ZEND_STRS("skips"),     cached->skips);
615    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cached->skips); /* deprecated */
616    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cached->ooms);
617    add_assoc_long_ex(return_value, ZEND_STRS("errors"),    cached->errors);
618
619    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cached->entries_count);
620    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cached->deletes_count);
621    if (interval) {
622        time_t gc = (cached->last_gc_expires + interval) - XG(request_time);
623        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    gc > 0 ? gc : 0);
624    }
625    else {
626        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
627    }
628    MAKE_STD_ZVAL(hits);
629    array_init(hits);
630    for (i = 0; i < sizeof(cached->hits_by_hour) / sizeof(cached->hits_by_hour[0]); i ++) {
631        add_next_index_long(hits, (long) cached->hits_by_hour[i]);
632    }
633    add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_hour"), hits);
634
635    MAKE_STD_ZVAL(hits);
636    array_init(hits);
637    for (i = 0; i < sizeof(cached->hits_by_second) / sizeof(cached->hits_by_second[0]); i ++) {
638        add_next_index_long(hits, (long) cached->hits_by_second[i]);
639    }
640    add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_second"), hits);
641
642    MAKE_STD_ZVAL(blocks);
643    array_init(blocks);
644
645    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
646    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
647    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
648
649    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
650        zval *bi;
651
652        MAKE_STD_ZVAL(bi);
653        array_init(bi);
654
655        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
656        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
657        add_next_index_zval(blocks, bi);
658#ifndef NDEBUG
659        avail += handlers->block_size(b);
660#endif
661    }
662    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
663#ifndef NDEBUG
664    assert(avail == handlers->avail(mem));
665#endif
666}
667/* }}} */
668static void xc_fillentry_unlocked(xc_entry_type_t type, const xc_entry_t *entry, xc_hash_value_t entryslotid, int del, zval *list TSRMLS_DC) /* {{{ */
669{
670    zval* ei;
671    const xc_entry_data_php_t *php;
672
673    ALLOC_INIT_ZVAL(ei);
674    array_init(ei);
675
676    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
677    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
678    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
679    add_assoc_long_ex(ei, ZEND_STRS("hvalue"),   entryslotid);
680    if (del) {
681        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
682    }
683#ifdef IS_UNICODE
684    do {
685        zval *zv;
686        ALLOC_INIT_ZVAL(zv);
687        switch (entry->name_type) {
688            case IS_UNICODE:
689                ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
690                break;
691            case IS_STRING:
692                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
693                break;
694            default:
695                assert(0);
696        }
697        zv->type = entry->name_type;
698        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
699    } while (0);
700#else
701    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
702#endif
703    switch (type) {
704        case XC_TYPE_PHP: {
705            xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
706            php = entry_php->php;
707            add_assoc_long_ex(ei, ZEND_STRS("size"),          entry->size + php->size);
708            add_assoc_long_ex(ei, ZEND_STRS("refcount"),      entry_php->refcount);
709            add_assoc_long_ex(ei, ZEND_STRS("phprefcount"),   php->refcount);
710            add_assoc_long_ex(ei, ZEND_STRS("file_mtime"),    entry_php->file_mtime);
711            add_assoc_long_ex(ei, ZEND_STRS("file_size"),     entry_php->file_size);
712            add_assoc_long_ex(ei, ZEND_STRS("file_device"),   entry_php->file_device);
713            add_assoc_long_ex(ei, ZEND_STRS("file_inode"),    entry_php->file_inode);
714
715#ifdef HAVE_XCACHE_CONSTANT
716            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
717#endif
718            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
719            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
720#ifdef ZEND_ENGINE_2_1
721            add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
722#endif
723            break;
724        }
725
726        case XC_TYPE_VAR:
727            add_assoc_long_ex(ei, ZEND_STRS("refcount"),      0); /* for BC only */
728            add_assoc_long_ex(ei, ZEND_STRS("size"),          entry->size);
729            break;
730
731        default:
732            assert(0);
733    }
734
735    add_next_index_zval(list, ei);
736}
737/* }}} */
738static void xc_filllist_unlocked(xc_entry_type_t type, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
739{
740    zval* list;
741    int i, c;
742    xc_entry_t *e;
743
744    ALLOC_INIT_ZVAL(list);
745    array_init(list);
746
747    for (i = 0, c = cache->hentry->size; i < c; i ++) {
748        for (e = cache->cached->entries[i]; e; e = e->next) {
749            xc_fillentry_unlocked(type, e, i, 0, list TSRMLS_CC);
750        }
751    }
752    add_assoc_zval(return_value, "cache_list", list);
753
754    ALLOC_INIT_ZVAL(list);
755    array_init(list);
756    for (e = cache->cached->deletes; e; e = e->next) {
757        xc_fillentry_unlocked(XC_TYPE_PHP, e, 0, 1, list TSRMLS_CC);
758    }
759    add_assoc_zval(return_value, "deleted_list", list);
760}
761/* }}} */
762
763static zend_op_array *xc_entry_install(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
764{
765    zend_uint i;
766    xc_entry_data_php_t *p = entry_php->php;
767    zend_op_array *old_active_op_array = CG(active_op_array);
768#ifndef ZEND_ENGINE_2
769    ALLOCA_FLAG(use_heap)
770    /* new ptr which is stored inside CG(class_table) */
771    xc_cest_t **new_cest_ptrs = (xc_cest_t **)my_do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt, use_heap);
772#endif
773
774    CG(active_op_array) = p->op_array;
775
776#ifdef HAVE_XCACHE_CONSTANT
777    /* install constant */
778    for (i = 0; i < p->constinfo_cnt; i ++) {
779        xc_constinfo_t *ci = &p->constinfos[i];
780        xc_install_constant(entry_php->entry.name.str.val, &ci->constant,
781                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
782    }
783#endif
784
785    /* install function */
786    for (i = 0; i < p->funcinfo_cnt; i ++) {
787        xc_funcinfo_t  *fi = &p->funcinfos[i];
788        xc_install_function(entry_php->entry.name.str.val, &fi->func,
789                UNISW(0, fi->type), fi->key, fi->key_size, fi->h TSRMLS_CC);
790    }
791
792    /* install class */
793    for (i = 0; i < p->classinfo_cnt; i ++) {
794        xc_classinfo_t *ci = &p->classinfos[i];
795#ifndef ZEND_ENGINE_2
796        zend_class_entry *ce = CestToCePtr(ci->cest);
797        /* fix pointer to the be which inside class_table */
798        if (ce->parent) {
799            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
800            assert(class_idx < i);
801            ci->cest.parent = new_cest_ptrs[class_idx];
802        }
803        new_cest_ptrs[i] =
804#endif
805#ifdef ZEND_COMPILE_DELAYED_BINDING
806        xc_install_class(entry_php->entry.name.str.val, &ci->cest, -1,
807                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
808#else
809        xc_install_class(entry_php->entry.name.str.val, &ci->cest, ci->oplineno,
810                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
811#endif
812    }
813
814#ifdef ZEND_ENGINE_2_1
815    /* trigger auto_globals jit */
816    for (i = 0; i < p->autoglobal_cnt; i ++) {
817        xc_autoglobal_t *aginfo = &p->autoglobals[i];
818        zend_u_is_auto_global(aginfo->type, aginfo->key, aginfo->key_len TSRMLS_CC);
819    }
820#endif
821#ifdef XCACHE_ERROR_CACHING
822    /* restore trigger errors */
823    for (i = 0; i < p->compilererror_cnt; i ++) {
824        xc_compilererror_t *error = &p->compilererrors[i];
825        CG(zend_lineno) = error->lineno;
826        zend_error(error->type, "%s", error->error);
827    }
828    CG(zend_lineno) = 0;
829#endif
830
831    i = 1;
832#ifndef ZEND_ENGINE_2_2
833    zend_hash_add(&EG(included_files), entry_php->entry.name.str.val, entry_php->entry.name.str.len+1, (void *)&i, sizeof(int), NULL);
834#endif
835
836#ifndef ZEND_ENGINE_2
837    my_free_alloca(new_cest_ptrs, use_heap);
838#endif
839    CG(active_op_array) = old_active_op_array;
840    return p->op_array;
841}
842/* }}} */
843
844static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t *caches, size_t cachecount TSRMLS_DC) /* {{{ */
845{
846    size_t i;
847    xc_stack_t *s;
848    xc_cache_t *cache;
849    xc_entry_php_t *entry_php;
850
851    for (i = 0; i < cachecount; i ++) {
852        s = &holds[i];
853        TRACE("holded %d items", xc_stack_count(s));
854        if (xc_stack_count(s)) {
855            cache = &caches[i];
856            ENTER_LOCK(cache) {
857                while (xc_stack_count(s)) {
858                    entry_php = (xc_entry_php_t *) xc_stack_pop(s);
859                    TRACE("unhold %d:%s", entry_php->file_inode, entry_php->entry.name.str.val);
860                    assert(entry_php->refcount > 0);
861                    --entry_php->refcount;
862                }
863            } LEAVE_LOCK(cache);
864        }
865    }
866}
867/* }}} */
868static void xc_entry_unholds(TSRMLS_D) /* {{{ */
869{
870    if (xc_php_caches) {
871        xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
872    }
873
874    if (xc_var_caches) {
875        xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
876    }
877}
878/* }}} */
879
880#define HASH(i) (i)
881#define HASH_ZSTR_L(t, s, l) HASH(zend_u_inline_hash_func((t), (s), ((l) + 1) * sizeof(UChar)))
882#define HASH_STR_S(s, l) HASH(zend_inline_hash_func((char *) (s), (l)))
883#define HASH_STR_L(s, l) HASH_STR_S((s), (l) + 1)
884#define HASH_STR(s) HASH_STR_L((s), strlen((s)) + 1)
885#define HASH_NUM(n) HASH(n)
886static inline xc_hash_value_t xc_hash_fold(xc_hash_value_t hvalue, const xc_hash_t *hasher) /* {{{ fold hash bits as needed */
887{
888    xc_hash_value_t folded = 0;
889    while (hvalue) {
890        folded ^= (hvalue & hasher->mask);
891        hvalue >>= hasher->bits;
892    }
893    return folded;
894}
895/* }}} */
896static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *entry TSRMLS_DC) /* {{{ */
897{
898    return UNISW(NOTHING, UG(unicode) ? HASH_ZSTR_L(entry->name_type, entry->name.uni.val, entry->name.uni.len) :)
899        HASH_STR_L(entry->name.str.val, entry->name.str.len);
900}
901/* }}} */
902#define xc_entry_hash_var xc_entry_hash_name
903static void xc_entry_free_key_php(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
904{
905#define X_FREE(var) do {\
906    if (entry_php->var) { \
907        efree(entry_php->var); \
908    } \
909} while (0)
910    X_FREE(dirpath);
911#ifdef IS_UNICODE
912    X_FREE(ufilepath);
913    X_FREE(udirpath);
914#endif
915
916#undef X_FREE
917}
918/* }}} */
919static char *xc_expand_url(const char *filepath, char *real_path TSRMLS_DC) /* {{{ */
920{
921    if (strstr(filepath, "://") != NULL) {
922        size_t filepath_len = strlen(filepath);
923        size_t copy_len = filepath_len > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : filepath_len;
924        memcpy(real_path, filepath, filepath_len);
925        real_path[copy_len] = '\0';
926        return real_path;
927    }
928    return expand_filepath(filepath, real_path TSRMLS_CC);
929}
930/* }}} */
931
932#define XC_RESOLVE_PATH_CHECKER(name) zend_bool name(const char *filepath, size_t filepath_len, void *data TSRMLS_DC)
933typedef XC_RESOLVE_PATH_CHECKER((*xc_resolve_path_checker_func_t));
934static zend_bool xc_resolve_path(const char *filepath, char *path_buffer, xc_resolve_path_checker_func_t checker_func, void *data TSRMLS_DC) /* {{{ */
935{
936    char *paths, *path;
937    char *tokbuf;
938    size_t path_buffer_len;
939    int size;
940    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
941    int ret;
942    ALLOCA_FLAG(use_heap)
943
944#if 0
945    if ((*filepath == '.' &&
946         (IS_SLASH(filepath[1]) ||
947          ((filepath[1] == '.') && IS_SLASH(filepath[2])))) ||
948        IS_ABSOLUTE_PATH(filepath, strlen(filepath)) ||
949        !path ||
950        !*path) {
951
952        if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
953            ret = 1;
954        }
955        else {
956            ret = FAILURE;
957        }
958        goto finish;
959    }
960#endif
961
962    size = strlen(PG(include_path)) + 1;
963    paths = (char *)my_do_alloca(size, use_heap);
964    memcpy(paths, PG(include_path), size);
965
966    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
967        path_buffer_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", path, filepath);
968        if (path_buffer_len < MAXPATHLEN - 1) {
969            if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
970                ret = 1;
971                goto finish;
972            }
973        }
974    }
975
976    /* fall back to current directory */
977    if (zend_is_executing(TSRMLS_C)) {
978        const char *executed_filename = zend_get_executed_filename(TSRMLS_C);
979        if (executed_filename && executed_filename[0] && executed_filename[0] != '[') {
980            size_t filename_len = strlen(filepath);
981            size_t dirname_len;
982
983            for (dirname_len = strlen(executed_filename) - 1; dirname_len > 0; --dirname_len) {
984                if (IS_SLASH(executed_filename[dirname_len])) {
985                    if (dirname_len + filename_len < MAXPATHLEN - 1) {
986                        ++dirname_len; /* include tailing slash */
987                        memcpy(path_buffer, executed_filename, dirname_len);
988                        memcpy(path_buffer + dirname_len, filepath, filename_len);
989                        path_buffer_len = dirname_len + filename_len;
990                        path_buffer[path_buffer_len] = '\0';
991                        if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
992                            ret = 1;
993                            goto finish;
994                        }
995                    }
996                    break;
997                }
998            }
999        }
1000    }
1001
1002    ret = 0;
1003
1004finish:
1005    my_free_alloca(paths, use_heap);
1006
1007    return ret;
1008}
1009/* }}} */
1010#ifndef ZEND_ENGINE_2_3
1011static XC_RESOLVE_PATH_CHECKER(xc_stat_file) /* {{{ */
1012{
1013    return VCWD_STAT(filepath, (struct stat *) data) == 0 ? 1 : 0;
1014}
1015/* }}} */
1016static int xc_resolve_path_stat(const char *filepath, char *path_buffer, struct stat *pbuf TSRMLS_DC) /* {{{ */
1017{
1018    return xc_resolve_path(filepath, path_buffer, xc_stat_file, (void *) pbuf TSRMLS_CC)
1019        ? SUCCESS
1020        : FAILURE;
1021}
1022/* }}} */
1023#endif
1024typedef struct xc_compiler_t { /* {{{ */
1025    /* XCache cached compile state */
1026    const char *filename;
1027    size_t filename_len;
1028    const char *opened_path;
1029    char opened_path_buffer[MAXPATHLEN];
1030
1031    xc_entry_hash_t entry_hash;
1032    xc_entry_php_t new_entry;
1033    xc_entry_data_php_t new_php;
1034} xc_compiler_t;
1035/* }}} */
1036typedef struct xc_entry_resolve_path_data_t { /* {{{ */
1037    xc_compiler_t *compiler;
1038    xc_entry_php_t **stored_entry;
1039} xc_entry_resolve_path_data_t;
1040/* }}} */
1041static XC_RESOLVE_PATH_CHECKER(xc_entry_resolve_path_func_unlocked) /* {{{ */
1042{
1043    xc_entry_resolve_path_data_t *entry_resolve_path_data = (xc_entry_resolve_path_data_t *) data;
1044    xc_compiler_t *compiler = entry_resolve_path_data->compiler;
1045
1046    compiler->new_entry.entry.name.str.val = xc_expand_url(filepath, compiler->opened_path_buffer TSRMLS_CC);
1047    compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
1048
1049    *entry_resolve_path_data->stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(
1050            XC_TYPE_PHP
1051            , &xc_php_caches[compiler->entry_hash.cacheid]
1052            , compiler->entry_hash.entryslotid
1053            , (xc_entry_t *) &compiler->new_entry
1054            TSRMLS_CC);
1055
1056    return *entry_resolve_path_data->stored_entry ? 1 : 0;
1057}
1058/* }}} */
1059static int xc_entry_resolve_path_unlocked(xc_compiler_t *compiler, const char *filepath, xc_entry_php_t **stored_entry TSRMLS_DC) /* {{{ */
1060{
1061    char path_buffer[MAXPATHLEN];
1062    xc_entry_resolve_path_data_t entry_resolve_path_data;
1063    entry_resolve_path_data.compiler = compiler;
1064    entry_resolve_path_data.stored_entry = stored_entry;
1065
1066    return xc_resolve_path(filepath, path_buffer, xc_entry_resolve_path_func_unlocked, (void *) &entry_resolve_path_data TSRMLS_CC)
1067        ? SUCCESS
1068        : FAILURE;
1069}
1070/* }}} */
1071static int xc_entry_php_quick_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1072{
1073    if (strcmp(SG(request_info).path_translated, compiler->filename) == 0) {
1074        /* sapi has already done this stat() for us */
1075        if (statbuf) {
1076            struct stat *sapi_stat = sapi_get_stat(TSRMLS_C);
1077            if (!sapi_stat) {
1078                goto giveupsapistat;
1079            }
1080            *statbuf = *sapi_stat;
1081        }
1082
1083        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1084        return SUCCESS;
1085    }
1086giveupsapistat:
1087
1088    /* absolute path */
1089    if (IS_ABSOLUTE_PATH(compiler->filename, strlen(compiler->filename))) {
1090        if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
1091            return FAILURE;
1092        }
1093        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1094        return SUCCESS;
1095    }
1096
1097    /* relative path */
1098    if (*compiler->filename == '.' && (IS_SLASH(compiler->filename[1]) || compiler->filename[1] == '.')) {
1099        const char *ptr = compiler->filename + 1;
1100        if (*ptr == '.') {
1101            while (*(++ptr) == '.');
1102            if (!IS_SLASH(*ptr)) {
1103                return FAILURE;
1104            }   
1105        }
1106
1107        if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
1108            return FAILURE;
1109        }
1110
1111        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1112        return SUCCESS;
1113    }
1114
1115    return FAILURE;
1116}
1117/* }}} */
1118static int xc_entry_php_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1119{
1120    if (xc_entry_php_quick_resolve_opened_path(compiler, statbuf TSRMLS_CC) == SUCCESS) {
1121        /* opened_path resolved */
1122        return SUCCESS;
1123    }
1124    /* fall back to real stat call */
1125    else {
1126#ifdef ZEND_ENGINE_2_3
1127        char *opened_path = php_resolve_path(compiler->filename, compiler->filename_len, PG(include_path) TSRMLS_CC);
1128        if (opened_path) {
1129            strcpy(compiler->opened_path_buffer, opened_path);
1130            efree(opened_path);
1131            compiler->opened_path = compiler->opened_path_buffer;
1132            if (!statbuf || VCWD_STAT(compiler->opened_path, statbuf) == 0) {
1133                return SUCCESS;
1134            }
1135        }
1136#else
1137        char path_buffer[MAXPATHLEN];
1138        if (xc_resolve_path_stat(compiler->filename, path_buffer, statbuf TSRMLS_CC) == SUCCESS) {
1139            compiler->opened_path = xc_expand_url(path_buffer, compiler->opened_path_buffer TSRMLS_CC);
1140            return SUCCESS;
1141        }
1142#endif
1143    }
1144    return FAILURE;
1145}
1146/* }}} */
1147static int xc_entry_php_init_key(xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1148{
1149    if (XG(stat)) {
1150        struct stat buf;
1151        time_t delta;
1152
1153        if (compiler->opened_path) {
1154            if (VCWD_STAT(compiler->opened_path, &buf) != 0) {
1155                return FAILURE;
1156            }
1157        }
1158        else {
1159            if (xc_entry_php_resolve_opened_path(compiler, &buf TSRMLS_CC) != SUCCESS) {
1160                return FAILURE;
1161            }
1162        }
1163
1164        delta = XG(request_time) - buf.st_mtime;
1165        if (abs(delta) < 2 && !xc_test) {
1166            return FAILURE;
1167        }
1168
1169        compiler->new_entry.file_mtime   = buf.st_mtime;
1170        compiler->new_entry.file_size    = buf.st_size;
1171        compiler->new_entry.file_device  = buf.st_dev;
1172        compiler->new_entry.file_inode   = buf.st_ino;
1173    }
1174    else {
1175        xc_entry_php_quick_resolve_opened_path(compiler, NULL TSRMLS_CC);
1176
1177        compiler->new_entry.file_mtime   = 0;
1178        compiler->new_entry.file_size    = 0;
1179        compiler->new_entry.file_device  = 0;
1180        compiler->new_entry.file_inode   = 0;
1181    }
1182
1183    {
1184        xc_hash_value_t basename_hash_value;
1185        if (xc_php_hcache.size > 1
1186         || !compiler->new_entry.file_inode) {
1187            const char *filename_end = compiler->filename + compiler->filename_len;
1188            const char *basename_begin = filename_end - 1;
1189
1190            /* scan till out of basename part */
1191            while (basename_begin >= compiler->filename && !IS_SLASH(*basename_begin)) {
1192                --basename_begin;
1193            }
1194            /* get back to basename_begin */
1195            ++basename_begin;
1196
1197            basename_hash_value = HASH_STR_L(basename_begin, filename_end - basename_begin);
1198        }
1199
1200        compiler->entry_hash.cacheid = xc_php_hcache.size > 1 ? xc_hash_fold(basename_hash_value, &xc_php_hcache) : 0;
1201        compiler->entry_hash.entryslotid = xc_hash_fold(
1202                compiler->new_entry.file_inode
1203                ? (xc_hash_value_t) HASH(compiler->new_entry.file_device + compiler->new_entry.file_inode)
1204                : basename_hash_value
1205                , &xc_php_hentry);
1206    }
1207
1208    compiler->new_entry.filepath  = NULL;
1209    compiler->new_entry.dirpath   = NULL;
1210#ifdef IS_UNICODE
1211    compiler->new_entry.ufilepath = NULL;
1212    compiler->new_entry.udirpath  = NULL;
1213#endif
1214
1215    return SUCCESS;
1216}
1217/* }}} */
1218static inline xc_hash_value_t xc_php_hash_md5(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1219{
1220    return HASH_STR_S(php->md5.digest, sizeof(php->md5.digest));
1221}
1222/* }}} */
1223static int xc_entry_data_php_init_md5(xc_cache_t *cache, xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1224{
1225    unsigned char   buf[1024];
1226    PHP_MD5_CTX     context;
1227    int             n;
1228    php_stream     *stream;
1229    ulong           old_rsid = EG(regular_list).nNextFreeElement;
1230
1231    stream = php_stream_open_wrapper((char *) compiler->filename, "rb", USE_PATH | REPORT_ERRORS | ENFORCE_SAFE_MODE | STREAM_DISABLE_OPEN_BASEDIR, NULL);
1232    if (!stream) {
1233        return FAILURE;
1234    }
1235
1236    PHP_MD5Init(&context);
1237    while ((n = php_stream_read(stream, (char *) buf, sizeof(buf))) > 0) {
1238        PHP_MD5Update(&context, buf, n);
1239    }
1240    PHP_MD5Final((unsigned char *) compiler->new_php.md5.digest, &context);
1241
1242    php_stream_close(stream);
1243    if (EG(regular_list).nNextFreeElement == old_rsid + 1) {
1244        EG(regular_list).nNextFreeElement = old_rsid;
1245    }
1246
1247    if (n < 0) {
1248        return FAILURE;
1249    }
1250
1251    compiler->new_php.hvalue = (xc_php_hash_md5(&compiler->new_php TSRMLS_CC) & cache->hphp->mask);
1252#ifdef XCACHE_DEBUG
1253    {
1254        char md5str[33];
1255        make_digest(md5str, (unsigned char *) compiler->new_php.md5.digest);
1256        TRACE("md5 %s", md5str);
1257    }
1258#endif
1259
1260    return SUCCESS;
1261}
1262/* }}} */
1263static void xc_entry_php_init(xc_entry_php_t *entry_php, const char *filepath TSRMLS_DC) /* {{{*/
1264{
1265    entry_php->filepath     = ZEND_24((char *), NOTHING) filepath;
1266    entry_php->filepath_len = strlen(entry_php->filepath);
1267    entry_php->dirpath      = estrndup(entry_php->filepath, entry_php->filepath_len);
1268    entry_php->dirpath_len  = zend_dirname(entry_php->dirpath, entry_php->filepath_len);
1269#ifdef IS_UNICODE
1270    zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->ufilepath, &entry_php->ufilepath_len, entry_php->filepath, entry_php->filepath_len TSRMLS_CC);
1271    zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->udirpath,  &entry_php->udirpath_len,  entry_php->dirpath,  entry_php->dirpath_len TSRMLS_CC);
1272#endif
1273}
1274/* }}} */
1275#ifndef ZEND_COMPILE_DELAYED_BINDING
1276static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
1277{
1278    char *class_name;
1279    zend_uint i;
1280    int class_len;
1281    xc_cest_t cest;
1282    xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
1283
1284    class_name = Z_OP_CONSTANT(opline->op1).value.str.val;
1285    class_len  = Z_OP_CONSTANT(opline->op1).value.str.len;
1286    if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
1287        assert(0);
1288    }
1289    TRACE("got ZEND_DECLARE_INHERITED_CLASS: %s", class_name + 1);
1290    /* let's see which class */
1291    for (i = 0; i < php->classinfo_cnt; i ++) {
1292        if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
1293            php->classinfos[i].oplineno = oplineno;
1294            php->have_early_binding = 1;
1295            break;
1296        }
1297    }
1298
1299    if (i == php->classinfo_cnt) {
1300        assert(0);
1301    }
1302}
1303/* }}} */
1304#endif
1305
1306/* {{{ Constant Usage */
1307#ifdef ZEND_ENGINE_2_4
1308#   define xcache_literal_is_dir  1
1309#   define xcache_literal_is_file 2
1310#else
1311#   define xcache_op1_is_file 1
1312#   define xcache_op1_is_dir  2
1313#   define xcache_op2_is_file 4
1314#   define xcache_op2_is_dir  8
1315#endif
1316typedef struct {
1317    zend_bool filepath_used;
1318    zend_bool dirpath_used;
1319    zend_bool ufilepath_used;
1320    zend_bool udirpath_used;
1321} xc_const_usage_t;
1322/* }}} */
1323static void xc_collect_op_array_info(xc_compiler_t *compiler, xc_const_usage_t *usage, xc_op_array_info_t *op_array_info, zend_op_array *op_array TSRMLS_DC) /* {{{ */
1324{
1325#ifdef ZEND_ENGINE_2_4
1326    int literalindex;
1327#else
1328    zend_uint oplinenum;
1329#endif
1330    xc_vector_t details;
1331
1332    xc_vector_init(xc_op_array_info_detail_t, &details);
1333
1334#define XCACHE_ANALYZE_LITERAL(type) \
1335    if (zend_binary_strcmp(Z_STRVAL(literal->constant), Z_STRLEN(literal->constant), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
1336        usage->type##path_used = 1; \
1337        literalinfo |= xcache_##literal##_is_##type; \
1338    }
1339
1340#define XCACHE_U_ANALYZE_LITERAL(type) \
1341    if (zend_u_##binary_strcmp(Z_USTRVAL(literal->constant), Z_USTRLEN(literal->constant), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
1342        usage->u##type##path_used = 1; \
1343        literalinfo |= xcache_##literal##_is_##type; \
1344    }
1345
1346#define XCACHE_ANALYZE_OP(type, op) \
1347    if (zend_binary_strcmp(Z_STRVAL(Z_OP_CONSTANT(opline->op)), Z_STRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
1348        usage->type##path_used = 1; \
1349        oplineinfo |= xcache_##op##_is_##type; \
1350    }
1351
1352#define XCACHE_U_ANALYZE_OP(type, op) \
1353    if (zend_u_##binary_strcmp(Z_USTRVAL(Z_OP_CONSTANT(opline->op)), Z_USTRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
1354        usage->u##type##path_used = 1; \
1355        oplineinfo |= xcache_##op##_is_##type; \
1356    }
1357
1358#ifdef ZEND_ENGINE_2_4
1359    for (literalindex = 0; literalindex < op_array->last_literal; literalindex++) {
1360        zend_literal *literal = &op_array->literals[literalindex];
1361        zend_uint literalinfo = 0;
1362        if (Z_TYPE(literal->constant) == IS_STRING) {
1363            XCACHE_ANALYZE_LITERAL(file)
1364            else XCACHE_ANALYZE_LITERAL(dir)
1365        }
1366#ifdef IS_UNICODE
1367        else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1368            XCACHE_U_ANALYZE_LITERAL(file)
1369            else XCACHE_U_ANALYZE_LITERAL(dir)
1370        }
1371#endif
1372        if (literalinfo) {
1373            xc_op_array_info_detail_t detail;
1374            detail.index = literalindex;
1375            detail.info  = literalinfo;
1376            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1377        }
1378    }
1379
1380    op_array_info->literalinfo_cnt = details.cnt;
1381    op_array_info->literalinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1382#else /* ZEND_ENGINE_2_4 */
1383    for (oplinenum = 0; oplinenum < op_array->last; oplinenum++) {
1384        zend_op *opline = &op_array->opcodes[oplinenum];
1385        zend_uint oplineinfo = 0;
1386        if (Z_OP_TYPE(opline->op1) == IS_CONST) {
1387            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1388                XCACHE_ANALYZE_OP(file, op1)
1389                else XCACHE_ANALYZE_OP(dir, op1)
1390            }
1391#ifdef IS_UNICODE
1392            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1393                XCACHE_U_ANALYZE_OP(file, op1)
1394                else XCACHE_U_ANALYZE_OP(dir, op1)
1395            }
1396#endif
1397        }
1398
1399        if (Z_OP_TYPE(opline->op2) == IS_CONST) {
1400            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1401                XCACHE_ANALYZE_OP(file, op2)
1402                else XCACHE_ANALYZE_OP(dir, op2)
1403            }
1404#ifdef IS_UNICODE
1405            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1406                XCACHE_U_ANALYZE_OP(file, op2)
1407                else XCACHE_U_ANALYZE_OP(dir, op2)
1408            }
1409#endif
1410        }
1411
1412        if (oplineinfo) {
1413            xc_op_array_info_detail_t detail;
1414            detail.index = oplinenum;
1415            detail.info  = oplineinfo;
1416            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1417        }
1418    }
1419
1420    op_array_info->oplineinfo_cnt = details.cnt;
1421    op_array_info->oplineinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1422#endif /* ZEND_ENGINE_2_4 */
1423    xc_vector_free(xc_op_array_info_detail_t, &details);
1424}
1425/* }}} */
1426void xc_fix_op_array_info(const xc_entry_php_t *entry_php, const xc_entry_data_php_t *php, zend_op_array *op_array, int shallow_copy, const xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
1427{
1428#ifdef ZEND_ENGINE_2_4
1429    zend_uint literalinfoindex;
1430
1431    for (literalinfoindex = 0; literalinfoindex < op_array_info->literalinfo_cnt; ++literalinfoindex) {
1432        int literalindex = op_array_info->literalinfos[literalinfoindex].index;
1433        int literalinfo = op_array_info->literalinfos[literalinfoindex].info;
1434        zend_literal *literal = &op_array->literals[literalindex];
1435        if ((literalinfo & xcache_literal_is_file)) {
1436            if (!shallow_copy) {
1437                efree(Z_STRVAL(literal->constant));
1438            }
1439            if (Z_TYPE(literal->constant) == IS_STRING) {
1440                assert(entry_php->filepath);
1441                ZVAL_STRINGL(&literal->constant, entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1442                TRACE("restored literal constant: %s", entry_php->filepath);
1443            }
1444#ifdef IS_UNICODE
1445            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1446                assert(entry_php->ufilepath);
1447                ZVAL_UNICODEL(&literal->constant, entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1448            }
1449#endif
1450            else {
1451                assert(0);
1452            }
1453        }
1454        else if ((literalinfo & xcache_literal_is_dir)) {
1455            if (!shallow_copy) {
1456                efree(Z_STRVAL(literal->constant));
1457            }
1458            if (Z_TYPE(literal->constant) == IS_STRING) {
1459                assert(entry_php->dirpath);
1460                TRACE("restored literal constant: %s", entry_php->dirpath);
1461                ZVAL_STRINGL(&literal->constant, entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1462            }
1463#ifdef IS_UNICODE
1464            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1465                assert(!entry_php->udirpath);
1466                ZVAL_UNICODEL(&literal->constant, entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1467            }
1468#endif
1469            else {
1470                assert(0);
1471            }
1472        }
1473    }
1474#else /* ZEND_ENGINE_2_4 */
1475    zend_uint oplinenum;
1476
1477    for (oplinenum = 0; oplinenum < op_array_info->oplineinfo_cnt; ++oplinenum) {
1478        int oplineindex = op_array_info->oplineinfos[oplinenum].index;
1479        int oplineinfo = op_array_info->oplineinfos[oplinenum].info;
1480        zend_op *opline = &op_array->opcodes[oplineindex];
1481        if ((oplineinfo & xcache_op1_is_file)) {
1482            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1483            if (!shallow_copy) {
1484                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1485            }
1486            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1487                assert(entry_php->filepath);
1488                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1489                TRACE("restored op1 constant: %s", entry_php->filepath);
1490            }
1491#ifdef IS_UNICODE
1492            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1493                assert(entry_php->ufilepath);
1494                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1495            }
1496#endif
1497            else {
1498                assert(0);
1499            }
1500        }
1501        else if ((oplineinfo & xcache_op1_is_dir)) {
1502            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1503            if (!shallow_copy) {
1504                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1505            }
1506            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1507                assert(entry_php->dirpath);
1508                TRACE("restored op1 constant: %s", entry_php->dirpath);
1509                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1510            }
1511#ifdef IS_UNICODE
1512            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1513                assert(!entry_php->udirpath);
1514                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1515            }
1516#endif
1517            else {
1518                assert(0);
1519            }
1520        }
1521
1522        if ((oplineinfo & xcache_op2_is_file)) {
1523            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1524            if (!shallow_copy) {
1525                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1526            }
1527            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1528                assert(entry_php->filepath);
1529                TRACE("restored op2 constant: %s", entry_php->filepath);
1530                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1531            }
1532#ifdef IS_UNICODE
1533            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1534                assert(entry_php->ufilepath);
1535                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1536            }
1537#endif
1538            else {
1539                assert(0);
1540            }
1541        }
1542        else if ((oplineinfo & xcache_op2_is_dir)) {
1543            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1544            if (!shallow_copy) {
1545                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1546            }
1547            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1548                assert(entry_php->dirpath);
1549                TRACE("restored op2 constant: %s", entry_php->dirpath);
1550                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1551            }
1552#ifdef IS_UNICODE
1553            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1554                assert(entry_php->udirpath);
1555                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1556            }
1557#endif
1558            else {
1559                assert(0);
1560            }
1561        }
1562    }
1563#endif /* ZEND_ENGINE_2_4 */
1564}
1565/* }}} */
1566static void xc_free_op_array_info(xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
1567{
1568#ifdef ZEND_ENGINE_2_4
1569    if (op_array_info->literalinfos) {
1570        efree(op_array_info->literalinfos);
1571    }
1572#else
1573    if (op_array_info->oplineinfos) {
1574        efree(op_array_info->oplineinfos);
1575    }
1576#endif
1577}
1578/* }}} */
1579static void xc_free_php(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1580{
1581    zend_uint i;
1582    if (php->classinfos) {
1583        for (i = 0; i < php->classinfo_cnt; i ++) {
1584            xc_classinfo_t *classinfo = &php->classinfos[i];
1585            zend_uint j;
1586
1587            for (j = 0; j < classinfo->methodinfo_cnt; j ++) {
1588                xc_free_op_array_info(&classinfo->methodinfos[j] TSRMLS_CC);
1589            }
1590
1591            if (classinfo->methodinfos) {
1592                efree(classinfo->methodinfos);
1593            }
1594        }
1595    }
1596    if (php->funcinfos) {
1597        for (i = 0; i < php->funcinfo_cnt; i ++) {
1598            xc_free_op_array_info(&php->funcinfos[i].op_array_info TSRMLS_CC);
1599        }
1600    }
1601    xc_free_op_array_info(&php->op_array_info TSRMLS_CC);
1602
1603#define X_FREE(var) do {\
1604    if (php->var) { \
1605        efree(php->var); \
1606    } \
1607} while (0)
1608
1609#ifdef ZEND_ENGINE_2_1
1610    X_FREE(autoglobals);
1611#endif
1612    X_FREE(classinfos);
1613    X_FREE(funcinfos);
1614#ifdef HAVE_XCACHE_CONSTANT
1615    X_FREE(constinfos);
1616#endif
1617#undef X_FREE
1618}
1619/* }}} */
1620static void xc_compile_php(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
1621{
1622    zend_uint old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
1623    zend_bool catched = 0;
1624
1625    /* {{{ compile */
1626    TRACE("compiling %s", h->opened_path ? h->opened_path : h->filename);
1627
1628    old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
1629    old_funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
1630    old_constinfo_cnt = zend_hash_num_elements(EG(zend_constants));
1631
1632    zend_try {
1633        compiler->new_php.op_array = old_compile_file(h, type TSRMLS_CC);
1634    } zend_catch {
1635        catched = 1;
1636    } zend_end_try();
1637
1638    if (catched) {
1639        goto err_bailout;
1640    }
1641
1642    if (compiler->new_php.op_array == NULL) {
1643        goto err_op_array;
1644    }
1645
1646    if (!XG(initial_compile_file_called)) {
1647        TRACE("%s", "!initial_compile_file_called, give up");
1648        return;
1649    }
1650
1651    /* }}} */
1652    /* {{{ prepare */
1653    zend_restore_compiled_filename(h->opened_path ? h->opened_path : (char *) h->filename TSRMLS_CC);
1654
1655#ifdef HAVE_XCACHE_CONSTANT
1656    compiler->new_php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
1657#endif
1658    compiler->new_php.funcinfo_cnt   = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
1659    compiler->new_php.classinfo_cnt  = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
1660#ifdef ZEND_ENGINE_2_1
1661    /* {{{ count new_php.autoglobal_cnt */ {
1662        Bucket *b;
1663
1664        compiler->new_php.autoglobal_cnt = 0;
1665        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1666            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1667            /* check if actived */
1668            if (auto_global->auto_global_callback && !auto_global->armed) {
1669                compiler->new_php.autoglobal_cnt ++;
1670            }
1671        }
1672    }
1673    /* }}} */
1674#endif
1675
1676#define X_ALLOC_N(var, cnt) do {     \
1677    if (compiler->new_php.cnt) {                  \
1678        ECALLOC_N(compiler->new_php.var, compiler->new_php.cnt); \
1679        if (!compiler->new_php.var) {             \
1680            goto err_alloc;          \
1681        }                            \
1682    }                                \
1683    else {                           \
1684        compiler->new_php.var = NULL;             \
1685    }                                \
1686} while (0)
1687
1688#ifdef HAVE_XCACHE_CONSTANT
1689    X_ALLOC_N(constinfos,  constinfo_cnt);
1690#endif
1691    X_ALLOC_N(funcinfos,   funcinfo_cnt);
1692    X_ALLOC_N(classinfos,  classinfo_cnt);
1693#ifdef ZEND_ENGINE_2_1
1694    X_ALLOC_N(autoglobals, autoglobal_cnt);
1695#endif
1696#undef X_ALLOC
1697    /* }}} */
1698
1699    /* {{{ shallow copy, pointers only */ {
1700        Bucket *b;
1701        zend_uint i;
1702        zend_uint j;
1703
1704#define COPY_H(vartype, var, cnt, name, datatype) do {        \
1705    for (i = 0, j = 0; b; i ++, b = b->pListNext) {           \
1706        vartype *data = &compiler->new_php.var[j];                         \
1707                                                              \
1708        if (i < old_##cnt) {                                  \
1709            continue;                                         \
1710        }                                                     \
1711        j ++;                                                 \
1712                                                              \
1713        assert(i < old_##cnt + compiler->new_php.cnt);                     \
1714        assert(b->pData);                                     \
1715        memcpy(&data->name, b->pData, sizeof(datatype));      \
1716        UNISW(NOTHING, data->type = b->key.type;)             \
1717        if (UNISW(1, b->key.type == IS_STRING)) {             \
1718            ZSTR_S(data->key)      = BUCKET_KEY_S(b);         \
1719        }                                                     \
1720        else {                                                \
1721            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
1722        }                                                     \
1723        data->key_size   = b->nKeyLength;                     \
1724        data->h          = b->h;                              \
1725    }                                                         \
1726} while(0)
1727
1728#ifdef HAVE_XCACHE_CONSTANT
1729        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1730#endif
1731        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1732        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1733
1734#undef COPY_H
1735
1736        /* for ZE1, cest need to be fixed inside store */
1737
1738#ifdef ZEND_ENGINE_2_1
1739        /* scan for acatived auto globals */
1740        i = 0;
1741        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1742            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1743            /* check if actived */
1744            if (auto_global->auto_global_callback && !auto_global->armed) {
1745                xc_autoglobal_t *data = &compiler->new_php.autoglobals[i];
1746
1747                assert(i < compiler->new_php.autoglobal_cnt);
1748                i ++;
1749                UNISW(NOTHING, data->type = b->key.type;)
1750                if (UNISW(1, b->key.type == IS_STRING)) {
1751                    ZSTR_S(data->key)     = BUCKET_KEY_S(b);
1752                }
1753                else {
1754                    ZSTR_U(data->key)     = BUCKET_KEY_U(b);
1755                }
1756                data->key_len = b->nKeyLength - 1;
1757                data->h       = b->h;
1758            }
1759        }
1760#endif
1761    }
1762    /* }}} */
1763
1764    /* {{{ collect info for file/dir path */ {
1765        Bucket *b;
1766        xc_const_usage_t const_usage;
1767        unsigned int i;
1768
1769        xc_entry_php_init(&compiler->new_entry, zend_get_compiled_filename(TSRMLS_C) TSRMLS_CC);
1770        memset(&const_usage, 0, sizeof(const_usage));
1771
1772        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1773            xc_classinfo_t *classinfo = &compiler->new_php.classinfos[i];
1774            zend_class_entry *ce = CestToCePtr(classinfo->cest);
1775            classinfo->methodinfo_cnt = ce->function_table.nTableSize;
1776            if (classinfo->methodinfo_cnt) {
1777                int j;
1778
1779                ECALLOC_N(classinfo->methodinfos, classinfo->methodinfo_cnt);
1780                if (!classinfo->methodinfos) {
1781                    goto err_alloc;
1782                }
1783
1784                for (j = 0, b = ce->function_table.pListHead; b; j ++, b = b->pListNext) {
1785                    xc_collect_op_array_info(compiler, &const_usage, &classinfo->methodinfos[j], (zend_op_array *) b->pData TSRMLS_CC);
1786                }
1787            }
1788            else {
1789                classinfo->methodinfos = NULL;
1790            }
1791        }
1792
1793        for (i = 0; i < compiler->new_php.funcinfo_cnt; i ++) {
1794            xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.funcinfos[i].op_array_info, (zend_op_array *) &compiler->new_php.funcinfos[i].func TSRMLS_CC);
1795        }
1796
1797        xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.op_array_info, compiler->new_php.op_array TSRMLS_CC);
1798
1799        /* file/dir path free unused */
1800#define X_FREE_UNUSED(var) \
1801        if (!const_usage.var##path_used) { \
1802            efree(compiler->new_entry.var##path); \
1803            compiler->new_entry.var##path = NULL; \
1804            compiler->new_entry.var##path_len = 0; \
1805        }
1806        /* filepath is required to restore op_array->filename, so no free filepath here */
1807        X_FREE_UNUSED(dir)
1808#ifdef IS_UNICODE
1809        X_FREE_UNUSED(ufile)
1810        X_FREE_UNUSED(udir)
1811#endif
1812#undef X_FREE_UNUSED
1813    }
1814    /* }}} */
1815#ifdef XCACHE_ERROR_CACHING
1816    compiler->new_php.compilererrors = xc_sandbox_compilererrors(TSRMLS_C);
1817    compiler->new_php.compilererror_cnt = xc_sandbox_compilererror_cnt(TSRMLS_C);
1818#endif
1819#ifndef ZEND_COMPILE_DELAYED_BINDING
1820    /* {{{ find inherited classes that should be early-binding */
1821    compiler->new_php.have_early_binding = 0;
1822    {
1823        zend_uint i;
1824        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1825            compiler->new_php.classinfos[i].oplineno = -1;
1826        }
1827    }
1828
1829    xc_undo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1830    xc_foreach_early_binding_class(compiler->new_php.op_array, xc_cache_early_binding_class_cb, (void *) &compiler->new_php TSRMLS_CC);
1831    xc_redo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1832    /* }}} */
1833#endif
1834
1835    return;
1836
1837err_alloc:
1838    xc_free_php(&compiler->new_php TSRMLS_CC);
1839
1840err_bailout:
1841err_op_array:
1842
1843    if (catched) {
1844        zend_bailout();
1845    }
1846}
1847/* }}} */
1848static zend_op_array *xc_compile_restore(xc_entry_php_t *stored_entry, xc_entry_data_php_t *stored_php TSRMLS_DC) /* {{{ */
1849{
1850    zend_op_array *op_array;
1851    xc_entry_php_t restored_entry;
1852    xc_entry_data_php_t restored_php;
1853    zend_bool catched;
1854    zend_uint i;
1855
1856    /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
1857    i = 1;
1858    zend_hash_add(&EG(included_files), stored_entry->entry.name.str.val, stored_entry->entry.name.str.len + 1, (void *)&i, sizeof(int), NULL);
1859
1860    CG(in_compilation)    = 1;
1861    CG(compiled_filename) = stored_entry->entry.name.str.val;
1862    CG(zend_lineno)       = 0;
1863    TRACE("restoring %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1864    xc_processor_restore_xc_entry_php_t(&restored_entry, stored_entry TSRMLS_CC);
1865    xc_processor_restore_xc_entry_data_php_t(stored_entry, &restored_php, stored_php, xc_readonly_protection TSRMLS_CC);
1866    restored_entry.php = &restored_php;
1867#ifdef SHOW_DPRINT
1868    xc_dprint(&restored_entry, 0 TSRMLS_CC);
1869#endif
1870
1871    catched = 0;
1872    zend_try {
1873        op_array = xc_entry_install(&restored_entry TSRMLS_CC);
1874    } zend_catch {
1875        catched = 1;
1876    } zend_end_try();
1877
1878#ifdef HAVE_XCACHE_CONSTANT
1879    if (restored_php.constinfos) {
1880        efree(restored_php.constinfos);
1881    }
1882#endif
1883    if (restored_php.funcinfos) {
1884        efree(restored_php.funcinfos);
1885    }
1886    if (restored_php.classinfos) {
1887        efree(restored_php.classinfos);
1888    }
1889
1890    if (catched) {
1891        zend_bailout();
1892    }
1893    CG(in_compilation)    = 0;
1894    CG(compiled_filename) = NULL;
1895    TRACE("restored %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1896    return op_array;
1897}
1898/* }}} */
1899typedef struct xc_sandboxed_compiler_t { /* {{{ */
1900    xc_compiler_t *compiler;
1901    /* input */
1902    zend_file_handle *h;
1903    int type;
1904
1905    /* sandbox output */
1906    xc_entry_php_t *stored_entry;
1907    xc_entry_data_php_t *stored_php;
1908} xc_sandboxed_compiler_t;
1909/* }}} */
1910
1911static zend_op_array *xc_compile_file_sandboxed(void *data TSRMLS_DC) /* {{{ */
1912{
1913    xc_sandboxed_compiler_t *sandboxed_compiler = (xc_sandboxed_compiler_t *) data;
1914    xc_compiler_t *compiler = sandboxed_compiler->compiler;
1915    zend_bool catched = 0;
1916    xc_cache_t *cache = &xc_php_caches[compiler->entry_hash.cacheid];
1917    xc_entry_php_t *stored_entry;
1918    xc_entry_data_php_t *stored_php;
1919
1920    /* {{{ compile */
1921    /* make compile inside sandbox */
1922#ifdef HAVE_XCACHE_CONSTANT
1923    compiler->new_php.constinfos  = NULL;
1924#endif
1925    compiler->new_php.funcinfos   = NULL;
1926    compiler->new_php.classinfos  = NULL;
1927#ifdef ZEND_ENGINE_2_1
1928    compiler->new_php.autoglobals = NULL;
1929#endif
1930    memset(&compiler->new_php.op_array_info, 0, sizeof(compiler->new_php.op_array_info));
1931
1932    XG(initial_compile_file_called) = 0;
1933    zend_try {
1934        compiler->new_php.op_array = NULL;
1935        xc_compile_php(compiler, sandboxed_compiler->h, sandboxed_compiler->type TSRMLS_CC);
1936    } zend_catch {
1937        catched = 1;
1938    } zend_end_try();
1939
1940    if (catched
1941     || !compiler->new_php.op_array /* possible ? */
1942     || !XG(initial_compile_file_called)) {
1943        goto err_aftersandbox;
1944    }
1945
1946    /* }}} */
1947#ifdef SHOW_DPRINT
1948    compiler->new_entry.php = &compiler->new_php;
1949    xc_dprint(&compiler->new_entry, 0 TSRMLS_CC);
1950#endif
1951
1952    stored_entry = NULL;
1953    stored_php = NULL;
1954    ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
1955        /* php_store */
1956        stored_php = xc_php_store_unlocked(cache, &compiler->new_php TSRMLS_CC);
1957        if (!stored_php) {
1958            /* error */
1959            break;
1960        }
1961        /* entry_store */
1962        compiler->new_entry.php = stored_php;
1963        stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
1964        if (stored_entry) {
1965            xc_php_addref_unlocked(stored_php);
1966            TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
1967            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
1968        }
1969    } LEAVE_LOCK_EX(cache);
1970    /* }}} */
1971    TRACE("%s", stored_entry ? "stored" : "store failed");
1972
1973    if (catched || !stored_php) {
1974        goto err_aftersandbox;
1975    }
1976
1977    cache->cached->compiling = 0;
1978    xc_free_php(&compiler->new_php TSRMLS_CC);
1979
1980    if (stored_entry) {
1981        sandboxed_compiler->stored_entry = stored_entry;
1982        sandboxed_compiler->stored_php = stored_php;
1983        /* discard newly compiled result, restore from stored one */
1984        if (compiler->new_php.op_array) {
1985#ifdef ZEND_ENGINE_2
1986            destroy_op_array(compiler->new_php.op_array TSRMLS_CC);
1987#else
1988            destroy_op_array(compiler->new_php.op_array);
1989#endif
1990            efree(compiler->new_php.op_array);
1991            compiler->new_php.op_array = NULL;
1992        }
1993        return NULL;
1994    }
1995    else {
1996        return compiler->new_php.op_array;
1997    }
1998
1999err_aftersandbox:
2000    xc_free_php(&compiler->new_php TSRMLS_CC);
2001
2002    cache->cached->compiling = 0;
2003    if (catched) {
2004        cache->cached->errors ++;
2005        zend_bailout();
2006    }
2007    return compiler->new_php.op_array;
2008} /* }}} */
2009static zend_op_array *xc_compile_file_cached(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2010{
2011    /*
2012    if (clog) {
2013        return old;
2014    }
2015
2016    if (cached_entry = getby entry_hash) {
2017        php = cached_entry.php;
2018        php = restore(php);
2019        return php;
2020    }
2021    else {
2022        if (!(php = getby md5)) {
2023            if (clog) {
2024                return old;
2025            }
2026
2027            inside_sandbox {
2028                php = compile;
2029                entry = create entries[entry];
2030            }
2031        }
2032
2033        entry.php = php;
2034        return php;
2035    }
2036    */
2037
2038    xc_entry_php_t *stored_entry;
2039    xc_entry_data_php_t *stored_php;
2040    zend_bool gaveup = 0;
2041    zend_bool catched = 0;
2042    zend_op_array *op_array;
2043    xc_cache_t *cache = &xc_php_caches[compiler->entry_hash.cacheid];
2044    xc_sandboxed_compiler_t sandboxed_compiler;
2045
2046    if (cache->cached->disabled) {
2047        return old_compile_file(h, type TSRMLS_CC);
2048    }
2049    /* stale skips precheck */
2050    if (cache->cached->disabled || XG(request_time) - cache->cached->compiling < 30) {
2051        cache->cached->skips ++;
2052        return old_compile_file(h, type TSRMLS_CC);
2053    }
2054
2055    /* {{{ entry_lookup/hit/md5_init/php_lookup */
2056    stored_entry = NULL;
2057    stored_php = NULL;
2058
2059    ENTER_LOCK_EX(cache) {
2060        if (!compiler->opened_path && xc_entry_resolve_path_unlocked(compiler, compiler->filename, &stored_entry TSRMLS_CC) == SUCCESS) {
2061            compiler->opened_path = compiler->new_entry.entry.name.str.val;
2062        }
2063        else {
2064            if (!compiler->opened_path && xc_entry_php_resolve_opened_path(compiler, NULL TSRMLS_CC) != SUCCESS) {
2065                gaveup = 1;
2066                break;
2067            }
2068
2069            /* finalize name */
2070            compiler->new_entry.entry.name.str.val = (char *) compiler->opened_path;
2071            compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
2072
2073            stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(XC_TYPE_PHP, cache, compiler->entry_hash.entryslotid, (xc_entry_t *) &compiler->new_entry TSRMLS_CC);
2074        }
2075
2076        if (stored_entry) {
2077            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2078
2079            TRACE(" hit %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2080            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2081            stored_php = stored_entry->php;
2082            break;
2083        }
2084
2085        TRACE("miss entry %d:%s", compiler->new_entry.file_inode, compiler->new_entry.entry.name.str.val);
2086
2087        if (xc_entry_data_php_init_md5(cache, compiler TSRMLS_CC) != SUCCESS) {
2088            gaveup = 1;
2089            break;
2090        }
2091
2092        stored_php = xc_php_find_unlocked(cache->cached, &compiler->new_php TSRMLS_CC);
2093
2094        if (stored_php) {
2095            compiler->new_entry.php = stored_php;
2096            xc_entry_php_init(&compiler->new_entry, compiler->opened_path TSRMLS_CC);
2097            stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
2098            if (stored_entry) {
2099                xc_php_addref_unlocked(stored_php);
2100                TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2101                xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2102            }
2103            else {
2104                gaveup = 1;
2105            }
2106            break;
2107        }
2108
2109        if (XG(request_time) - cache->cached->compiling < 30) {
2110            TRACE("%s", "miss php, but compiling");
2111            cache->cached->skips ++;
2112            gaveup = 1;
2113            break;
2114        }
2115
2116        TRACE("%s", "miss php, going to compile");
2117        cache->cached->compiling = XG(request_time);
2118    } LEAVE_LOCK_EX(cache);
2119
2120    if (catched) {
2121        cache->cached->compiling = 0;
2122        zend_bailout();
2123    }
2124
2125    /* found entry */
2126    if (stored_entry && stored_php) {
2127        zend_llist_add_element(&CG(open_files), h);
2128        return xc_compile_restore(stored_entry, stored_php TSRMLS_CC);
2129    }
2130
2131    /* gaveup */
2132    if (gaveup) {
2133        return old_compile_file(h, type TSRMLS_CC);
2134    }
2135    /* }}} */
2136
2137    sandboxed_compiler.compiler = compiler;
2138    sandboxed_compiler.h = h;
2139    sandboxed_compiler.type = type;
2140    sandboxed_compiler.stored_php = NULL;
2141    sandboxed_compiler.stored_entry = NULL;
2142    op_array = xc_sandbox(xc_compile_file_sandboxed, (void *) &sandboxed_compiler, h->opened_path ? h->opened_path : h->filename TSRMLS_CC);
2143    if (sandboxed_compiler.stored_entry) {
2144        return xc_compile_restore(sandboxed_compiler.stored_entry, sandboxed_compiler.stored_php TSRMLS_CC);
2145    }
2146    else {
2147        return op_array;
2148    }
2149}
2150/* }}} */
2151static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2152{
2153    xc_compiler_t compiler;
2154    zend_op_array *op_array;
2155
2156    assert(xc_initized);
2157
2158    TRACE("xc_compile_file: type=%d name=%s", h->type, h->filename ? h->filename : "NULL");
2159
2160    if (!XG(cacher)
2161     || !h->filename
2162     || !SG(request_info).path_translated
2163     || strstr(h->filename, "://") != NULL
2164#ifdef ZEND_ENGINE_2_3
2165     /* supported by php_resolve_path */
2166     || (!XG(stat) && strstr(PG(include_path), "://") != NULL)
2167#else
2168     || strstr(PG(include_path), "://") != NULL
2169#endif
2170     ) {
2171        TRACE("%s", "cacher not enabled");
2172        return old_compile_file(h, type TSRMLS_CC);
2173    }
2174
2175    /* {{{ entry_init_key */
2176    compiler.opened_path = h->opened_path;
2177    compiler.filename = compiler.opened_path ? compiler.opened_path : h->filename;
2178    compiler.filename_len = strlen(compiler.filename);
2179    if (xc_entry_php_init_key(&compiler TSRMLS_CC) != SUCCESS) {
2180        TRACE("failed to init key for %s", compiler.filename);
2181        return old_compile_file(h, type TSRMLS_CC);
2182    }
2183    /* }}} */
2184
2185    op_array = xc_compile_file_cached(&compiler, h, type TSRMLS_CC);
2186
2187    xc_entry_free_key_php(&compiler.new_entry TSRMLS_CC);
2188
2189    return op_array;
2190}
2191/* }}} */
2192
2193/* gdb helper functions, but N/A for coredump */
2194int xc_is_rw(const void *p) /* {{{ */
2195{
2196    xc_shm_t *shm;
2197    size_t i;
2198
2199    if (xc_php_caches) {
2200        for (i = 0; i < xc_php_hcache.size; i ++) {
2201            shm = xc_php_caches[i].shm;
2202            if (shm->handlers->is_readwrite(shm, p)) {
2203                return 1;
2204            }
2205        }
2206    }
2207
2208    if (xc_var_caches) {
2209        for (i = 0; i < xc_var_hcache.size; i ++) {
2210            shm = xc_var_caches[i].shm;
2211            if (shm->handlers->is_readwrite(shm, p)) {
2212                return 1;
2213            }
2214        }
2215    }
2216    return 0;
2217}
2218/* }}} */
2219int xc_is_ro(const void *p) /* {{{ */
2220{
2221    xc_shm_t *shm;
2222    size_t i;
2223
2224    if (xc_php_caches) {
2225        for (i = 0; i < xc_php_hcache.size; i ++) {
2226            shm = xc_php_caches[i].shm;
2227            if (shm->handlers->is_readonly(shm, p)) {
2228                return 1;
2229            }
2230        }
2231    }
2232
2233    if (xc_var_caches) {
2234        for (i = 0; i < xc_var_hcache.size; i ++) {
2235            shm = xc_var_caches[i].shm;
2236            if (shm->handlers->is_readonly(shm, p)) {
2237                return 1;
2238            }
2239        }
2240    }
2241    return 0;
2242}
2243/* }}} */
2244int xc_is_shm(const void *p) /* {{{ */
2245{
2246    return xc_is_ro(p) || xc_is_rw(p);
2247}
2248/* }}} */
2249
2250void xc_gc_add_op_array(xc_gc_op_array_t *gc_op_array TSRMLS_DC) /* {{{ */
2251{
2252    zend_llist_add_element(&XG(gc_op_arrays), (void *) gc_op_array);
2253}
2254/* }}} */
2255static void xc_gc_op_array(void *pDest) /* {{{ */
2256{
2257    xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
2258    zend_uint i;
2259#ifdef ZEND_ENGINE_2
2260    if (op_array->arg_info) {
2261        for (i = 0; i < op_array->num_args; i++) {
2262            efree((char *) ZSTR_V(op_array->arg_info[i].name));
2263            if (ZSTR_V(op_array->arg_info[i].class_name)) {
2264                efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
2265            }
2266        }
2267        efree(op_array->arg_info);
2268    }
2269#endif
2270    if (op_array->opcodes) {
2271        efree(op_array->opcodes);
2272    }
2273}
2274/* }}} */
2275
2276/* module helper function */
2277static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
2278{
2279    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2280    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2281    return 0;
2282}
2283/* }}} */
2284static xc_shm_t *xc_cache_destroy(xc_cache_t *caches, xc_hash_t *hcache) /* {{{ */
2285{
2286    size_t i;
2287    xc_shm_t *shm = NULL;
2288
2289    assert(caches);
2290
2291    for (i = 0; i < hcache->size; i ++) {
2292        xc_cache_t *cache = &caches[i];
2293        if (cache) {
2294            if (cache->lck) {
2295                xc_lock_destroy(cache->lck);
2296            }
2297            /* do NOT touch cached data */
2298            shm = cache->shm;
2299            cache->shm->handlers->memdestroy(cache->mem);
2300        }
2301    }
2302    free(caches);
2303    return shm;
2304}
2305/* }}} */
2306static xc_cache_t *xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_hash_t *hphp, xc_shmsize_t shmsize) /* {{{ */
2307{
2308    xc_cache_t *caches = NULL;
2309    xc_mem_t *mem;
2310    time_t now = time(NULL);
2311    size_t i;
2312    xc_memsize_t memsize;
2313
2314    memsize = shmsize / hcache->size;
2315
2316    /* Don't let it break out of mem after ALIGNed
2317     * This is important for
2318     * Simply loop until it fit our need
2319     */
2320    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
2321        if (memsize < ALIGN(1)) {
2322            CHECK(NULL, "cache too small");
2323        }
2324        memsize --;
2325    }
2326
2327    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t)), "caches OOM");
2328
2329    for (i = 0; i < hcache->size; i ++) {
2330        xc_cache_t *cache = &caches[i];
2331        CHECK(mem                     = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
2332        CHECK(cache->cached           = mem->handlers->calloc(mem, 1, sizeof(xc_cached_t)), "cache OOM");
2333        CHECK(cache->cached->entries  = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
2334        if (hphp) {
2335            CHECK(cache->cached->phps = mem->handlers->calloc(mem, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
2336        }
2337        CHECK(cache->lck              = xc_lock_init(NULL), "can't create lock");
2338
2339        cache->hcache  = hcache;
2340        cache->hentry  = hentry;
2341        cache->hphp    = hphp;
2342        cache->shm     = shm;
2343        cache->mem     = mem;
2344        cache->cacheid = i;
2345        cache->cached->last_gc_deletes = now;
2346        cache->cached->last_gc_expires = now;
2347    }
2348    return caches;
2349
2350err:
2351    if (caches) {
2352        xc_cache_destroy(caches, hcache);
2353    }
2354    return NULL;
2355}
2356/* }}} */
2357static void xc_destroy() /* {{{ */
2358{
2359    xc_shm_t *shm = NULL;
2360    if (old_compile_file && zend_compile_file == xc_compile_file) {
2361        zend_compile_file = old_compile_file;
2362        old_compile_file = NULL;
2363    }
2364
2365    if (xc_php_caches) {
2366        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
2367        xc_php_caches = NULL;
2368    }
2369
2370    if (xc_var_caches) {
2371        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
2372        xc_var_caches = NULL;
2373    }
2374
2375    if (shm) {
2376        xc_shm_destroy(shm);
2377    }
2378
2379    xc_initized = 0;
2380}
2381/* }}} */
2382static int xc_init() /* {{{ */
2383{
2384    xc_shm_t *shm = NULL;
2385    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
2386
2387    xc_php_caches = xc_var_caches = NULL;
2388
2389    if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
2390        zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
2391        goto err;
2392    }
2393
2394    if (xc_php_size || xc_var_size) {
2395        CHECK(shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
2396        if (!shm->handlers->can_readonly(shm)) {
2397            xc_readonly_protection = 0;
2398        }
2399
2400        if (xc_php_size) {
2401            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, &xc_php_hentry, xc_php_size), "failed init opcode cache");
2402        }
2403
2404        if (xc_var_size) {
2405            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, NULL, xc_var_size), "failed init variable cache");
2406        }
2407    }
2408    return SUCCESS;
2409
2410err:
2411    if (xc_php_caches || xc_var_caches) {
2412        xc_destroy();
2413        /* shm destroied in xc_destroy() */
2414    }
2415    else if (shm) {
2416        xc_destroy();
2417        xc_shm_destroy(shm);
2418        shm = NULL;
2419    }
2420    return 0;
2421}
2422/* }}} */
2423static void xc_request_init(TSRMLS_D) /* {{{ */
2424{
2425    size_t i;
2426
2427    if (!XG(internal_table_copied)) {
2428        zend_function tmp_func;
2429        xc_cest_t tmp_cest;
2430
2431#ifdef HAVE_XCACHE_CONSTANT
2432        zend_hash_destroy(&XG(internal_constant_table));
2433#endif
2434        zend_hash_destroy(&XG(internal_function_table));
2435        zend_hash_destroy(&XG(internal_class_table));
2436
2437#ifdef HAVE_XCACHE_CONSTANT
2438        zend_hash_init_ex(&XG(internal_constant_table), 20,  NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2439#endif
2440        zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
2441        zend_hash_init_ex(&XG(internal_class_table),    10,  NULL, NULL, 1, 0);
2442
2443#ifdef HAVE_XCACHE_CONSTANT
2444        xc_copy_internal_zend_constants(&XG(internal_constant_table), EG(zend_constants));
2445#endif
2446        zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
2447        zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
2448
2449        XG(internal_table_copied) = 1;
2450    }
2451    if (xc_php_caches && !XG(php_holds)) {
2452        XG(php_holds_size) = xc_php_hcache.size;
2453        XG(php_holds) = calloc(XG(php_holds_size), sizeof(xc_stack_t));
2454        for (i = 0; i < xc_php_hcache.size; i ++) {
2455            xc_stack_init(&XG(php_holds[i]));
2456        }
2457    }
2458
2459    if (xc_var_caches && !XG(var_holds)) {
2460        XG(var_holds_size) = xc_var_hcache.size;
2461        XG(var_holds) = calloc(XG(var_holds_size), sizeof(xc_stack_t));
2462        for (i = 0; i < xc_var_hcache.size; i ++) {
2463            xc_stack_init(&XG(var_holds[i]));
2464        }
2465    }
2466
2467#ifdef ZEND_ENGINE_2
2468    zend_llist_init(&XG(gc_op_arrays), sizeof(xc_gc_op_array_t), xc_gc_op_array, 0);
2469#endif
2470
2471#if PHP_API_VERSION <= 20041225
2472    XG(request_time) = time(NULL);
2473#else
2474    XG(request_time) = sapi_get_request_time(TSRMLS_C);
2475#endif
2476}
2477/* }}} */
2478static void xc_request_shutdown(TSRMLS_D) /* {{{ */
2479{
2480    xc_entry_unholds(TSRMLS_C);
2481    xc_gc_expires_php(TSRMLS_C);
2482    xc_gc_expires_var(TSRMLS_C);
2483    xc_gc_deletes(TSRMLS_C);
2484#ifdef ZEND_ENGINE_2
2485    zend_llist_destroy(&XG(gc_op_arrays));
2486#endif
2487}
2488/* }}} */
2489
2490/* user functions */
2491static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
2492{
2493    zval **server = NULL;
2494    zval **user = NULL;
2495    zval **pass = NULL;
2496    char *admin_user = NULL;
2497    char *admin_pass = NULL;
2498    HashTable *ht;
2499
2500    /* auth disabled, nothing to do.. */
2501    if (!xc_admin_enable_auth) {
2502        return 1;
2503    }
2504
2505    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
2506        admin_user = NULL;
2507    }
2508    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
2509        admin_pass = NULL;
2510    }
2511
2512    if (admin_user == NULL || admin_pass == NULL) {
2513        php_error_docref(XCACHE_WIKI_URL "/InstallAdministration" TSRMLS_CC, E_ERROR,
2514                "xcache.admin.user and/or xcache.admin.pass settings is not configured."
2515                " Make sure you've modified the correct php ini file for your php used in webserver.");
2516        zend_bailout();
2517    }
2518    if (strlen(admin_pass) != 32) {
2519        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.pass is %lu chars unexpectedly, it is supposed to be the password after md5() which should be 32 chars", (unsigned long) strlen(admin_pass));
2520        zend_bailout();
2521    }
2522
2523#ifdef ZEND_ENGINE_2_1
2524    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
2525#endif
2526    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
2527        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
2528        zend_bailout();
2529    }
2530    ht = HASH_OF((*server));
2531
2532    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
2533        user = NULL;
2534    }
2535    else if (Z_TYPE_PP(user) != IS_STRING) {
2536        user = NULL;
2537    }
2538
2539    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
2540        pass = NULL;
2541    }
2542    else if (Z_TYPE_PP(pass) != IS_STRING) {
2543        pass = NULL;
2544    }
2545
2546    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
2547        PHP_MD5_CTX context;
2548        char md5str[33];
2549        unsigned char digest[16];
2550
2551        PHP_MD5Init(&context);
2552        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
2553        PHP_MD5Final(digest, &context);
2554
2555        md5str[0] = '\0';
2556        make_digest(md5str, digest);
2557        if (strcmp(admin_pass, md5str) == 0) {
2558            return 1;
2559        }
2560    }
2561
2562#define STR "HTTP/1.0 401 Unauthorized"
2563    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2564#undef STR
2565#define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
2566    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2567#undef STR
2568#define STR "Content-type: text/html; charset=UTF-8"
2569    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2570#undef STR
2571    ZEND_PUTS("<html>\n");
2572    ZEND_PUTS("<head><title>XCache Authentication Failed</title></head>\n");
2573    ZEND_PUTS("<body>\n");
2574    ZEND_PUTS("<h1>XCache Authentication Failed</h1>\n");
2575    ZEND_PUTS("<p>You're not authorized to access this page due to wrong username and/or password you typed.<br />The following check points is suggested:</p>\n");
2576    ZEND_PUTS("<ul>\n");
2577    ZEND_PUTS("<li>Be aware that `Username' and `Password' is case sense. Check capslock status led on your keyboard, and punch left/right Shift keys once for each</li>\n");
2578    ZEND_PUTS("<li>Make sure the md5 password is generated correctly. You may use <a href=\"mkpassword.php\">mkpassword.php</a></li>\n");
2579    ZEND_PUTS("<li>Reload browser cache by pressing F5 and/or Ctrl+F5, or simply clear browser cache after you've updated username/password in php ini.</li>\n");
2580    ZEND_PUTS("</ul>\n");
2581    ZEND_PUTS("Check <a href=\"" XCACHE_WIKI_URL "/InstallAdministration\">XCache wiki page</a> for more information.\n");
2582    ZEND_PUTS("</body>\n");
2583    ZEND_PUTS("</html>\n");
2584
2585    zend_bailout();
2586    return 0;
2587}
2588/* }}} */
2589static void xc_clear(long type, xc_cache_t *cache TSRMLS_DC) /* {{{ */
2590{
2591    xc_entry_t *e, *next;
2592    int entryslotid, c;
2593
2594    ENTER_LOCK(cache) {
2595        for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
2596            for (e = cache->cached->entries[entryslotid]; e; e = next) {
2597                next = e->next;
2598                xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
2599            }
2600            cache->cached->entries[entryslotid] = NULL;
2601        }
2602    } LEAVE_LOCK(cache);
2603} /* }}} */
2604/* {{{ xcache_admin_operate */
2605typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR, XC_OP_ENABLE } xcache_op_type;
2606static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
2607{
2608    long type;
2609    long size;
2610    xc_cache_t *caches, *cache;
2611    long id = 0;
2612    zend_bool enable = 1;
2613
2614    xcache_admin_auth_check(TSRMLS_C);
2615
2616    if (!xc_initized) {
2617        RETURN_NULL();
2618    }
2619
2620    switch (optype) {
2621        case XC_OP_COUNT:
2622            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2623                return;
2624            }
2625            break;
2626
2627        case XC_OP_CLEAR:
2628            id = -1;
2629            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &type, &id) == FAILURE) {
2630                return;
2631            }
2632            break;
2633
2634        case XC_OP_ENABLE:
2635            id = -1;
2636            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lb", &type, &id, &enable) == FAILURE) {
2637                return;
2638            }
2639            break;
2640
2641        default:
2642            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
2643                return;
2644            }
2645    }
2646
2647    switch (type) {
2648        case XC_TYPE_PHP:
2649            size = xc_php_hcache.size;
2650            caches = xc_php_caches;
2651            break;
2652
2653        case XC_TYPE_VAR:
2654            size = xc_var_hcache.size;
2655            caches = xc_var_caches;
2656            break;
2657
2658        default:
2659            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
2660            RETURN_FALSE;
2661    }
2662
2663    switch (optype) {
2664        case XC_OP_COUNT:
2665            RETURN_LONG(caches ? size : 0)
2666            break;
2667
2668        case XC_OP_INFO:
2669        case XC_OP_LIST:
2670            if (!caches || id < 0 || id >= size) {
2671                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2672                RETURN_FALSE;
2673            }
2674
2675            array_init(return_value);
2676
2677            cache = &caches[id];
2678            ENTER_LOCK(cache) {
2679                if (optype == XC_OP_INFO) {
2680                    xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
2681                }
2682                else {
2683                    xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
2684                }
2685            } LEAVE_LOCK(cache);
2686            break;
2687
2688        case XC_OP_CLEAR:
2689            if (!caches || id < -1 || id >= size) {
2690                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2691                RETURN_FALSE;
2692            }
2693
2694            if (id == -1) {
2695                for (id = 0; id < size; ++id) {
2696                    xc_clear(type, &caches[id] TSRMLS_CC);
2697                }
2698            }
2699            else {
2700                xc_clear(type, &caches[id] TSRMLS_CC);
2701            }
2702
2703            xc_gc_deletes(TSRMLS_C);
2704            break;
2705
2706        case XC_OP_ENABLE:
2707            if (!caches || id < -1 || id >= size) {
2708                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2709                RETURN_FALSE;
2710            }
2711
2712            if (id == -1) {
2713                for (id = 0; id < size; ++id) {
2714                    caches[id].cached->disabled = !enable ? XG(request_time) : 0;
2715                }
2716            }
2717            else {
2718                caches[id].cached->disabled = !enable ? XG(request_time) : 0;
2719            }
2720
2721            break;
2722
2723        default:
2724            assert(0);
2725    }
2726}
2727/* }}} */
2728/* {{{ proto int xcache_count(int type)
2729   Return count of cache on specified cache type */
2730PHP_FUNCTION(xcache_count)
2731{
2732    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2733}
2734/* }}} */
2735/* {{{ proto array xcache_info(int type, int id)
2736   Get cache info by id on specified cache type */
2737PHP_FUNCTION(xcache_info)
2738{
2739    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2740}
2741/* }}} */
2742/* {{{ proto array xcache_list(int type, int id)
2743   Get cache entries list by id on specified cache type */
2744PHP_FUNCTION(xcache_list)
2745{
2746    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2747}
2748/* }}} */
2749/* {{{ proto array xcache_clear_cache(int type, [ int id = -1 ])
2750   Clear cache by id on specified cache type */
2751PHP_FUNCTION(xcache_clear_cache)
2752{
2753    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2754}
2755/* }}} */
2756/* {{{ proto array xcache_enable_cache(int type, [ int id = -1, [ bool enable = true ] ])
2757   Enable or disable cache by id on specified cache type */
2758PHP_FUNCTION(xcache_enable_cache)
2759{
2760    xcache_admin_operate(XC_OP_ENABLE, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2761}
2762/* }}} */
2763
2764#define VAR_CACHE_NOT_INITIALIZED() do { \
2765        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache var cache was not initialized properly. Check php log for actual reason"); \
2766} while (0)
2767
2768static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
2769{
2770    xc_hash_value_t hv;
2771
2772    switch (name->type) {
2773#ifdef IS_UNICODE
2774        case IS_UNICODE:
2775        case IS_STRING:
2776#endif
2777        default:
2778#ifdef IS_UNICODE
2779            convert_to_unicode(name);
2780#else
2781            convert_to_string(name);
2782#endif
2783    }
2784
2785#ifdef IS_UNICODE
2786    entry_var->name_type = name->type;
2787#endif
2788    entry_var->entry.name = name->value;
2789
2790    hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
2791
2792    entry_hash->cacheid = (hv & xc_var_hcache.mask);
2793    hv >>= xc_var_hcache.bits;
2794    entry_hash->entryslotid = (hv & xc_var_hentry.mask);
2795    return SUCCESS;
2796}
2797/* }}} */
2798/* {{{ proto mixed xcache_get(string name)
2799   Get cached data by specified name */
2800PHP_FUNCTION(xcache_get)
2801{
2802    xc_entry_hash_t entry_hash;
2803    xc_cache_t *cache;
2804    xc_entry_var_t entry_var, *stored_entry_var;
2805    zval *name;
2806
2807    if (!xc_var_caches) {
2808        VAR_CACHE_NOT_INITIALIZED();
2809        RETURN_NULL();
2810    }
2811
2812    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2813        return;
2814    }
2815    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2816    cache = &xc_var_caches[entry_hash.cacheid];
2817
2818    if (cache->cached->disabled) {
2819        RETURN_NULL();
2820    }
2821
2822    ENTER_LOCK(cache) {
2823        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2824        if (stored_entry_var) {
2825            /* return */
2826            xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
2827            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2828        }
2829        else {
2830            RETVAL_NULL();
2831        }
2832    } LEAVE_LOCK(cache);
2833}
2834/* }}} */
2835/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
2836   Store data to cache by specified name */
2837PHP_FUNCTION(xcache_set)
2838{
2839    xc_entry_hash_t entry_hash;
2840    xc_cache_t *cache;
2841    xc_entry_var_t entry_var, *stored_entry_var;
2842    zval *name;
2843    zval *value;
2844
2845    if (!xc_var_caches) {
2846        VAR_CACHE_NOT_INITIALIZED();
2847        RETURN_NULL();
2848    }
2849
2850    entry_var.entry.ttl = XG(var_ttl);
2851    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
2852        return;
2853    }
2854
2855    if (Z_TYPE_P(value) == IS_OBJECT) {
2856        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Objects cannot be stored in the variable cache. Use serialize before xcache_set");
2857        RETURN_NULL();
2858    }
2859
2860    /* max ttl */
2861    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
2862        entry_var.entry.ttl = xc_var_maxttl;
2863    }
2864
2865    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2866    cache = &xc_var_caches[entry_hash.cacheid];
2867
2868    if (cache->cached->disabled) {
2869        RETURN_NULL();
2870    }
2871
2872    ENTER_LOCK(cache) {
2873        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2874        if (stored_entry_var) {
2875            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2876        }
2877        entry_var.value = value;
2878        RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
2879    } LEAVE_LOCK(cache);
2880}
2881/* }}} */
2882/* {{{ proto bool  xcache_isset(string name)
2883   Check if an entry exists in cache by specified name */
2884PHP_FUNCTION(xcache_isset)
2885{
2886    xc_entry_hash_t entry_hash;
2887    xc_cache_t *cache;
2888    xc_entry_var_t entry_var, *stored_entry_var;
2889    zval *name;
2890
2891    if (!xc_var_caches) {
2892        VAR_CACHE_NOT_INITIALIZED();
2893        RETURN_FALSE;
2894    }
2895
2896    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2897        return;
2898    }
2899    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2900    cache = &xc_var_caches[entry_hash.cacheid];
2901
2902    if (cache->cached->disabled) {
2903        RETURN_FALSE;
2904    }
2905
2906    ENTER_LOCK(cache) {
2907        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2908        if (stored_entry_var) {
2909            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2910            RETVAL_TRUE;
2911            /* return */
2912        }
2913        else {
2914            RETVAL_FALSE;
2915        }
2916
2917    } LEAVE_LOCK(cache);
2918}
2919/* }}} */
2920/* {{{ proto bool  xcache_unset(string name)
2921   Unset existing data in cache by specified name */
2922PHP_FUNCTION(xcache_unset)
2923{
2924    xc_entry_hash_t entry_hash;
2925    xc_cache_t *cache;
2926    xc_entry_var_t entry_var, *stored_entry_var;
2927    zval *name;
2928
2929    if (!xc_var_caches) {
2930        VAR_CACHE_NOT_INITIALIZED();
2931        RETURN_FALSE;
2932    }
2933
2934    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2935        return;
2936    }
2937    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2938    cache = &xc_var_caches[entry_hash.cacheid];
2939
2940    if (cache->cached->disabled) {
2941        RETURN_FALSE;
2942    }
2943
2944    ENTER_LOCK(cache) {
2945        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2946        if (stored_entry_var) {
2947            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2948            RETVAL_TRUE;
2949        }
2950        else {
2951            RETVAL_FALSE;
2952        }
2953    } LEAVE_LOCK(cache);
2954}
2955/* }}} */
2956/* {{{ proto bool  xcache_unset_by_prefix(string prefix)
2957   Unset existing data in cache by specified prefix */
2958PHP_FUNCTION(xcache_unset_by_prefix)
2959{
2960    zval *prefix;
2961    int i, iend;
2962
2963    if (!xc_var_caches) {
2964        VAR_CACHE_NOT_INITIALIZED();
2965        RETURN_FALSE;
2966    }
2967
2968    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
2969        return;
2970    }
2971
2972    for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
2973        xc_cache_t *cache = &xc_var_caches[i];
2974        if (cache->cached->disabled) {
2975            continue;
2976        }
2977
2978        ENTER_LOCK(cache) {
2979            int entryslotid, jend;
2980            for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
2981                xc_entry_t *entry, *next;
2982                for (entry = cache->cached->entries[entryslotid]; entry; entry = next) {
2983                    next = entry->next;
2984                    if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
2985                        xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
2986                    }
2987                }
2988            }
2989        } LEAVE_LOCK(cache);
2990    }
2991}
2992/* }}} */
2993static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
2994{
2995    xc_entry_hash_t entry_hash;
2996    xc_cache_t *cache;
2997    xc_entry_var_t entry_var, *stored_entry_var;
2998    zval *name;
2999    long count = 1;
3000    long value = 0;
3001    zval oldzval;
3002
3003    if (!xc_var_caches) {
3004        VAR_CACHE_NOT_INITIALIZED();
3005        RETURN_NULL();
3006    }
3007
3008    entry_var.entry.ttl = XG(var_ttl);
3009    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
3010        return;
3011    }
3012
3013    /* max ttl */
3014    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
3015        entry_var.entry.ttl = xc_var_maxttl;
3016    }
3017
3018    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
3019    cache = &xc_var_caches[entry_hash.cacheid];
3020
3021    if (cache->cached->disabled) {
3022        RETURN_NULL();
3023    }
3024
3025    ENTER_LOCK(cache) {
3026        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
3027        if (stored_entry_var) {
3028            TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
3029            /* do it in place */
3030            if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
3031                zval *zv;
3032                stored_entry_var->entry.ctime = XG(request_time);
3033                stored_entry_var->entry.ttl   = entry_var.entry.ttl;
3034                TRACE("%s", "incdec: islong");
3035                value = Z_LVAL_P(stored_entry_var->value);
3036                value += (inc == 1 ? count : - count);
3037                RETVAL_LONG(value);
3038
3039                zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
3040                Z_LVAL_P(zv) = value;
3041                ++cache->cached->updates;
3042                break; /* leave lock */
3043            }
3044
3045            TRACE("%s", "incdec: notlong");
3046            xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
3047            convert_to_long(&oldzval);
3048            value = Z_LVAL(oldzval);
3049            zval_dtor(&oldzval);
3050        }
3051        else {
3052            TRACE("incdec: %s not found", entry_var.entry.name.str.val);
3053        }
3054
3055        value += (inc == 1 ? count : - count);
3056        RETVAL_LONG(value);
3057        entry_var.value = return_value;
3058
3059        if (stored_entry_var) {
3060            entry_var.entry.atime = stored_entry_var->entry.atime;
3061            entry_var.entry.ctime = stored_entry_var->entry.ctime;
3062            entry_var.entry.hits  = stored_entry_var->entry.hits;
3063            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
3064        }
3065        xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
3066    } LEAVE_LOCK(cache);
3067}
3068/* }}} */
3069/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
3070   Increase an int counter in cache by specified name, create it if not exists */
3071PHP_FUNCTION(xcache_inc)
3072{
3073    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3074}
3075/* }}} */
3076/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
3077   Decrease an int counter in cache by specified name, create it if not exists */
3078PHP_FUNCTION(xcache_dec)
3079{
3080    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3081}
3082/* }}} */
3083static zend_function_entry xcache_cacher_functions[] = /* {{{ */
3084{
3085    PHP_FE(xcache_count,             NULL)
3086    PHP_FE(xcache_info,              NULL)
3087    PHP_FE(xcache_list,              NULL)
3088    PHP_FE(xcache_clear_cache,       NULL)
3089    PHP_FE(xcache_enable_cache,      NULL)
3090    PHP_FE(xcache_get,               NULL)
3091    PHP_FE(xcache_set,               NULL)
3092    PHP_FE(xcache_isset,             NULL)
3093    PHP_FE(xcache_unset,             NULL)
3094    PHP_FE(xcache_unset_by_prefix,   NULL)
3095    PHP_FE(xcache_inc,               NULL)
3096    PHP_FE(xcache_dec,               NULL)
3097    PHP_FE_END
3098};
3099/* }}} */
3100
3101static int xc_cacher_zend_startup(zend_extension *extension) /* {{{ */
3102{
3103    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
3104        if (xc_init() != SUCCESS) {
3105            zend_error(E_ERROR, "XCache: Cannot init");
3106            return FAILURE;
3107        }
3108        xc_initized = 1;
3109        xc_init_time = time(NULL);
3110#ifdef PHP_WIN32
3111        xc_init_instance_id = GetCurrentProcessId();
3112#else
3113        xc_init_instance_id = getpid();
3114#endif
3115#ifdef ZTS
3116        xc_init_instance_subid = tsrm_thread_id();
3117#endif
3118    }
3119
3120    if (xc_php_size) {
3121        old_compile_file = zend_compile_file;
3122        zend_compile_file = xc_compile_file;
3123    }
3124
3125    return SUCCESS;
3126}
3127/* }}} */
3128static void xc_cacher_zend_shutdown(zend_extension *extension) /* {{{ */
3129{
3130    if (xc_initized) {
3131        xc_destroy();
3132    }
3133}
3134/* }}} */
3135/* {{{ zend extension definition structure */
3136static zend_extension xc_cacher_zend_extension_entry = {
3137    XCACHE_NAME " Cacher",
3138    XCACHE_VERSION,
3139    XCACHE_AUTHOR,
3140    XCACHE_URL,
3141    XCACHE_COPYRIGHT,
3142    xc_cacher_zend_startup,
3143    xc_cacher_zend_shutdown,
3144    NULL,           /* activate_func_t */
3145    NULL,           /* deactivate_func_t */
3146    NULL,           /* message_handler_func_t */
3147    NULL,           /* op_array_handler_func_t */
3148    NULL,           /* statement_handler_func_t */
3149    NULL,           /* fcall_begin_handler_func_t */
3150    NULL,           /* fcall_end_handler_func_t */
3151    NULL,           /* op_array_ctor_func_t */
3152    NULL,           /* op_array_dtor_func_t */
3153    STANDARD_ZEND_EXTENSION_PROPERTIES
3154};
3155/* }}} */
3156
3157/* {{{ ini */
3158#ifdef ZEND_WIN32
3159#   define DEFAULT_PATH "xcache"
3160#else
3161#   define DEFAULT_PATH "/dev/zero"
3162#endif
3163PHP_INI_BEGIN()
3164    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_mmap_path)
3165    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_readonly_protection)
3166    /* opcode cache */
3167    PHP_INI_ENTRY1     ("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_admin_enable_auth)
3168    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3169    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3170    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3171    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_shm_scheme)
3172    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_ttl)
3173    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_gc_interval)
3174    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,    cacher, zend_xcache_globals, xcache_globals)
3175    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,    stat,   zend_xcache_globals, xcache_globals)
3176    /* var cache */
3177    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3178    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3179    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3180    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_maxttl)
3181    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_gc_interval)
3182    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong, var_ttl,   zend_xcache_globals, xcache_globals)
3183PHP_INI_END()
3184/* }}} */
3185static PHP_MINFO_FUNCTION(xcache_cacher) /* {{{ */
3186{
3187    char buf[100];
3188    char *ptr;
3189    int left, len;
3190    xc_shm_scheme_t *scheme;
3191
3192    php_info_print_table_start();
3193    php_info_print_table_row(2, "XCache Cacher Module", "enabled");
3194    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "disabled");
3195#ifdef ZEND_ENGINE_2_1
3196    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
3197    php_info_print_table_row(2, "Cache Init Time", ptr);
3198    efree(ptr);
3199#else
3200    snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
3201    php_info_print_table_row(2, "Cache Init Time", buf);
3202#endif
3203
3204#ifdef ZTS
3205    snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
3206#else
3207    snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
3208#endif
3209    php_info_print_table_row(2, "Cache Instance Id", buf);
3210
3211    if (xc_php_size) {
3212        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
3213        snprintf(buf, sizeof(buf), "enabled, %s bytes, %lu split(s), with %lu slots each", ptr, (unsigned long) xc_php_hcache.size, xc_php_hentry.size);
3214        php_info_print_table_row(2, "Opcode Cache", buf);
3215        efree(ptr);
3216    }
3217    else {
3218        php_info_print_table_row(2, "Opcode Cache", "disabled");
3219    }
3220    if (xc_var_size) {
3221        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
3222        snprintf(buf, sizeof(buf), "enabled, %s bytes, %lu split(s), with %lu slots each", ptr, (unsigned long) xc_var_hcache.size, xc_var_hentry.size);
3223        php_info_print_table_row(2, "Variable Cache", buf);
3224        efree(ptr);
3225    }
3226    else {
3227        php_info_print_table_row(2, "Variable Cache", "disabled");
3228    }
3229
3230    left = sizeof(buf);
3231    ptr = buf;
3232    buf[0] = '\0';
3233    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
3234        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
3235        left -= len;
3236        ptr += len;
3237    }
3238    php_info_print_table_row(2, "Shared Memory Schemes", buf);
3239
3240    php_info_print_table_end();
3241
3242    DISPLAY_INI_ENTRIES();
3243}
3244/* }}} */
3245static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
3246{
3247    size_t bits, size;
3248    char *value;
3249
3250    if (cfg_get_string(name, &value) != SUCCESS) {
3251        value = default_value;
3252    }
3253
3254    p->size = zend_atoi(value, strlen(value));
3255    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
3256        /* empty body */
3257    }
3258    p->size = size;
3259    p->bits = bits;
3260    p->mask = size - 1;
3261
3262    return SUCCESS;
3263}
3264/* }}} */
3265static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
3266{
3267    char *value;
3268
3269    if (cfg_get_string(name, &value) != SUCCESS) {
3270        value = default_value;
3271    }
3272
3273    *p = zend_atol(value, strlen(value));
3274    return SUCCESS;
3275}
3276/* }}} */
3277static PHP_MINIT_FUNCTION(xcache_cacher) /* {{{ */
3278{
3279    char *env;
3280    zend_extension *ext;
3281    zend_llist_position lpos;
3282
3283    ext = zend_get_extension("Zend Optimizer");
3284    if (ext) {
3285        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
3286#if 0
3287        ext->op_array_handler = NULL;
3288#endif
3289    }
3290    /* cache if there's an op_array_ctor */
3291    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
3292            ext;
3293            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
3294        if (ext->op_array_ctor) {
3295            xc_have_op_array_ctor = 1;
3296            break;
3297        }
3298    }
3299
3300    /* additional_functions requires PHP 5.3. TODO: find simpler way to do it */
3301#ifdef ZEND_ENGINE_2_3
3302    if (strcmp(sapi_module.name, "cgi-fcgi") == 0 && !sapi_module.additional_functions && !getenv("XCACHE_SKIP_FCGI_WARNING") && !getenv("GATEWAY_INTERFACE")) {
3303        if ((getenv("PHP_FCGI_CHILDREN") == NULL) || (atoi(getenv("PHP_FCGI_CHILDREN")) < 1)) {
3304            zend_error(E_WARNING, "PHP_FCGI_CHILDREN should be >= 1 and use 1 group of parent/childs model. Set XCACHE_SKIP_FCGI_WARNING=1 to skip this warning. See " XCACHE_WIKI_URL "/Faq");
3305        }
3306    }
3307#endif
3308
3309    xc_config_long(&xc_php_size,       "xcache.size",        "0");
3310    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
3311    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
3312
3313    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
3314    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
3315    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
3316
3317    if (strcmp(sapi_module.name, "cli") == 0) {
3318        if ((env = getenv("XCACHE_TEST")) != NULL) {
3319            xc_test = atoi(env);
3320        }
3321        if (!xc_test) {
3322            /* disable cache for cli except for testing */
3323            xc_php_size = xc_var_size = 0;
3324        }
3325    }
3326
3327    if (xc_php_size <= 0) {
3328        xc_php_size = xc_php_hcache.size = 0;
3329    }
3330    if (xc_var_size <= 0) {
3331        xc_var_size = xc_var_hcache.size = 0;
3332    }
3333
3334    xc_init_constant(module_number TSRMLS_CC);
3335
3336    REGISTER_INI_ENTRIES();
3337
3338    xc_sandbox_module_init(module_number TSRMLS_CC);
3339    return xcache_zend_extension_add(&xc_cacher_zend_extension_entry, 0);
3340
3341err_init:
3342    return FAILURE;
3343}
3344/* }}} */
3345static PHP_MSHUTDOWN_FUNCTION(xcache_cacher) /* {{{ */
3346{
3347    xc_sandbox_module_shutdown();
3348
3349    xcache_zend_extension_remove(&xc_cacher_zend_extension_entry);
3350    UNREGISTER_INI_ENTRIES();
3351
3352    if (xc_mmap_path) {
3353        pefree(xc_mmap_path, 1);
3354        xc_mmap_path = NULL;
3355    }
3356    if (xc_shm_scheme) {
3357        pefree(xc_shm_scheme, 1);
3358        xc_shm_scheme = NULL;
3359    }
3360
3361    return SUCCESS;
3362}
3363/* }}} */
3364static PHP_RINIT_FUNCTION(xcache_cacher) /* {{{ */
3365{
3366    xc_request_init(TSRMLS_C);
3367    return SUCCESS;
3368}
3369/* }}} */
3370/* {{{ static PHP_RSHUTDOWN_FUNCTION(xcache_cacher) */
3371#ifndef ZEND_ENGINE_2
3372static PHP_RSHUTDOWN_FUNCTION(xcache_cacher)
3373#else
3374static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache_cacher)
3375#endif
3376{
3377#ifdef ZEND_ENGINE_2
3378    TSRMLS_FETCH();
3379#endif
3380
3381    xc_request_shutdown(TSRMLS_C);
3382    return SUCCESS;
3383}
3384/* }}} */
3385static zend_module_entry xcache_cacher_module_entry = { /* {{{ */
3386    STANDARD_MODULE_HEADER,
3387    XCACHE_NAME " Cacher",
3388    xcache_cacher_functions,
3389    PHP_MINIT(xcache_cacher),
3390    PHP_MSHUTDOWN(xcache_cacher),
3391    PHP_RINIT(xcache_cacher),
3392#ifndef ZEND_ENGINE_2
3393    PHP_RSHUTDOWN(xcache_cacher),
3394#else
3395    NULL,
3396#endif
3397    PHP_MINFO(xcache_cacher),
3398    XCACHE_VERSION,
3399#ifdef PHP_GINIT
3400    NO_MODULE_GLOBALS,
3401#endif
3402#ifdef ZEND_ENGINE_2
3403    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache_cacher),
3404#else
3405    NULL,
3406    NULL,
3407#endif
3408    STANDARD_MODULE_PROPERTIES_EX
3409};
3410/* }}} */
3411int xc_cacher_startup_module() /* {{{ */
3412{
3413    return zend_startup_module(&xcache_cacher_module_entry);
3414}
3415/* }}} */
3416void xc_cacher_disable() /* {{{ */
3417{
3418    time_t now = time(NULL);
3419    size_t i;
3420
3421    if (xc_php_caches) {
3422        for (i = 0; i < xc_php_hcache.size; i ++) {
3423            if (xc_php_caches[i].cached) {
3424                xc_php_caches[i].cached->disabled = now;
3425            }
3426        }
3427    }
3428
3429    if (xc_var_caches) {
3430        for (i = 0; i < xc_var_hcache.size; i ++) {
3431            if (xc_var_caches[i].cached) {
3432                xc_var_caches[i].cached->disabled = now;
3433            }
3434        }
3435    }
3436}
3437/* }}} */
Note: See TracBrowser for help on using the repository browser.