source: trunk/mod_cacher/xc_cacher.c @ 1135

Last change on this file since 1135 was 1135, checked in by moo, 2 years ago

refactor: s/mem/allocator/

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