source: trunk/mod_cacher/xc_cacher.c @ 1140

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

closes #198: support for caching protocol url

  • Property svn:eol-style set to native
File size: 101.4 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(strstr(entry1->name.str.val, "://") != NULL || IS_ABSOLUTE_PATH(entry1->name.str.val, entry1->name.str.len));
246            assert(strstr(entry1->name.str.val, "://") != NULL || 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) int name(const char *filepath, size_t filepath_len, void *data TSRMLS_DC)
950typedef XC_RESOLVE_PATH_CHECKER((*xc_resolve_path_checker_func_t));
951static int 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        ret = checker_func(path_buffer, path_buffer_len, data TSRMLS_CC);
970        goto finish;
971    }
972#endif
973
974    size = strlen(PG(include_path)) + 1;
975    paths = (char *)my_do_alloca(size, use_heap);
976    memcpy(paths, PG(include_path), size);
977
978    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
979        path_buffer_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", path, filepath);
980        if (path_buffer_len < MAXPATHLEN - 1) {
981            ret = checker_func(path_buffer, path_buffer_len, data TSRMLS_CC);
982            if (ret == SUCCESS) {
983                goto finish;
984            }
985        }
986    }
987
988    /* fall back to current directory */
989    if (zend_is_executing(TSRMLS_C)) {
990        const char *executing_filename = zend_get_executed_filename(TSRMLS_C);
991        int dirname_len = strlen(executing_filename);
992        size_t filename_len = strlen(filepath);
993
994        while ((--dirname_len >= 0) && !IS_SLASH(executing_filename[dirname_len]));
995        if (executing_filename && dirname_len > 0 && executing_filename[0] && executing_filename[0] != '['
996         && dirname_len + 1 + filename_len + 1 < MAXPATHLEN) {
997            memcpy(path_buffer, executing_filename, dirname_len + 1);
998            memcpy(path_buffer + dirname_len + 1, filepath, filename_len + 1);
999            path_buffer_len = dirname_len + 1 + filename_len;
1000            ret = checker_func(path_buffer, path_buffer_len, data TSRMLS_CC);
1001            if (ret == SUCCESS) {
1002                goto finish;
1003            }
1004        }
1005    }
1006
1007    ret = FAILURE;
1008
1009finish:
1010    my_free_alloca(paths, use_heap);
1011
1012    return ret;
1013}
1014/* }}} */
1015
1016static zend_bool xc_is_absolute(const char *filepath, size_t filepath_len) /* {{{ */
1017{
1018    const char *p;
1019
1020    if (IS_ABSOLUTE_PATH(filepath, filepath_len)) {
1021        return 1;
1022    }
1023
1024    for (p = filepath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
1025    if ((*p == ':') && (p - filepath > 1) && (p[1] == '/') && (p[2] == '/')) {
1026        return 1;
1027    }
1028
1029    return 0;
1030}
1031/* }}} */
1032static int xc_stat(const char *filepath, struct stat *statbuf TSRMLS_DC) /* {{{ */
1033{
1034    if (strstr(filepath, "://") != NULL) {
1035        php_stream_statbuf ssb; 
1036        php_stream_wrapper *wrapper = NULL; 
1037        char *path_for_open = NULL; 
1038
1039        wrapper = php_stream_locate_url_wrapper(filepath, &path_for_open, 0 TSRMLS_CC); 
1040        if (wrapper && wrapper->wops->url_stat
1041         && wrapper != &php_plain_files_wrapper
1042         && wrapper->wops->url_stat(wrapper, path_for_open, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL TSRMLS_CC) == SUCCESS) {
1043            *statbuf = ssb.sb;
1044            return SUCCESS;
1045        }
1046
1047        return FAILURE;
1048    }
1049
1050    return VCWD_STAT(filepath, statbuf);
1051}
1052/* }}} */
1053static XC_RESOLVE_PATH_CHECKER(xc_resolve_path_stat_checker) /* {{{ */
1054{
1055    return xc_stat(filepath, (struct stat *) data TSRMLS_CC);
1056}
1057/* }}} */
1058#ifndef ZEND_ENGINE_2_3
1059static int xc_resolve_path_stat(const char *filepath, char *path_buffer, struct stat *pbuf TSRMLS_DC) /* {{{ */
1060{
1061    return xc_resolve_path(filepath, path_buffer, xc_resolve_path_stat_checker, (void *) pbuf TSRMLS_CC);
1062}
1063/* }}} */
1064#endif
1065typedef struct xc_compiler_t { /* {{{ */
1066    /* XCache cached compile state */
1067    const char *filename;
1068    size_t filename_len;
1069    const char *opened_path;
1070    char opened_path_buffer[MAXPATHLEN];
1071
1072    xc_entry_hash_t entry_hash;
1073    xc_entry_php_t new_entry;
1074    xc_entry_data_php_t new_php;
1075} xc_compiler_t;
1076/* }}} */
1077typedef struct xc_resolve_path_entry_checker_t { /* {{{ */
1078    xc_compiler_t *compiler;
1079    xc_entry_php_t **stored_entry;
1080} xc_resolve_path_entry_checker_data_t;
1081/* }}} */
1082static XC_RESOLVE_PATH_CHECKER(xc_resolve_path_entry_checker) /* {{{ */
1083{
1084    xc_resolve_path_entry_checker_data_t *entry_checker_data = (xc_resolve_path_entry_checker_data_t *) data;
1085    xc_compiler_t *compiler = entry_checker_data->compiler;
1086
1087    compiler->new_entry.entry.name.str.val = xc_expand_url(filepath, compiler->opened_path_buffer TSRMLS_CC);
1088    compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
1089
1090    *entry_checker_data->stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(
1091            XC_TYPE_PHP
1092            , &xc_php_caches[compiler->entry_hash.cacheid]
1093            , compiler->entry_hash.entryslotid
1094            , (xc_entry_t *) &compiler->new_entry
1095            TSRMLS_CC);
1096
1097    return *entry_checker_data->stored_entry ? SUCCESS : FAILURE;
1098}
1099/* }}} */
1100static int xc_resolve_path_check_entry_unlocked(xc_compiler_t *compiler, const char *filepath, xc_entry_php_t **stored_entry TSRMLS_DC) /* {{{ */
1101{
1102    char path_buffer[MAXPATHLEN];
1103    xc_resolve_path_entry_checker_data_t entry_checker_data;
1104    entry_checker_data.compiler = compiler;
1105    entry_checker_data.stored_entry = stored_entry;
1106
1107    return xc_resolve_path(filepath, path_buffer, xc_resolve_path_entry_checker, (void *) &entry_checker_data TSRMLS_CC);
1108}
1109/* }}} */
1110static int xc_entry_php_quick_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1111{
1112    if (strcmp(SG(request_info).path_translated, compiler->filename) == 0) {
1113        /* sapi has already done this stat() for us */
1114        if (statbuf) {
1115            struct stat *sapi_stat = sapi_get_stat(TSRMLS_C);
1116            if (!sapi_stat) {
1117                goto giveupsapistat;
1118            }
1119            *statbuf = *sapi_stat;
1120        }
1121
1122        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1123        return SUCCESS;
1124    }
1125giveupsapistat:
1126
1127    /* absolute path */
1128    if (xc_is_absolute(compiler->filename, strlen(compiler->filename))) {
1129        if (statbuf && xc_stat(compiler->filename, statbuf TSRMLS_CC) != SUCCESS) {
1130            return FAILURE;
1131        }
1132        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1133        return SUCCESS;
1134    }
1135
1136    /* relative path */
1137    if (*compiler->filename == '.' && (IS_SLASH(compiler->filename[1]) || compiler->filename[1] == '.')) {
1138        const char *ptr = compiler->filename + 1;
1139        if (*ptr == '.') {
1140            while (*(++ptr) == '.');
1141            if (!IS_SLASH(*ptr)) {
1142                return FAILURE;
1143            }   
1144        }
1145
1146        if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
1147            return FAILURE;
1148        }
1149
1150        compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
1151        return SUCCESS;
1152    }
1153
1154    return FAILURE;
1155}
1156/* }}} */
1157static int xc_entry_php_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
1158{
1159    if (xc_entry_php_quick_resolve_opened_path(compiler, statbuf TSRMLS_CC) == SUCCESS) {
1160        /* opened_path resolved */
1161        return SUCCESS;
1162    }
1163    /* fall back to real stat call */
1164    else {
1165#ifdef ZEND_ENGINE_2_3
1166        char *opened_path = php_resolve_path(compiler->filename, compiler->filename_len, PG(include_path) TSRMLS_CC);
1167        if (opened_path) {
1168            strcpy(compiler->opened_path_buffer, opened_path);
1169            efree(opened_path);
1170            compiler->opened_path = compiler->opened_path_buffer;
1171            if (!statbuf || xc_stat(compiler->opened_path, statbuf TSRMLS_CC) == SUCCESS) {
1172                return SUCCESS;
1173            }
1174        }
1175#else
1176        char path_buffer[MAXPATHLEN];
1177        if (xc_resolve_path_stat(compiler->filename, path_buffer, statbuf TSRMLS_CC) == SUCCESS) {
1178            compiler->opened_path = xc_expand_url(path_buffer, compiler->opened_path_buffer TSRMLS_CC);
1179            return SUCCESS;
1180        }
1181#endif
1182    }
1183    return FAILURE;
1184}
1185/* }}} */
1186static int xc_entry_php_init_key(xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1187{
1188    if (XG(stat)) {
1189        struct stat buf;
1190        time_t delta;
1191
1192        if (compiler->opened_path) {
1193            if (xc_stat(compiler->opened_path, &buf TSRMLS_CC) != SUCCESS) {
1194                return FAILURE;
1195            }
1196        }
1197        else {
1198            if (xc_entry_php_resolve_opened_path(compiler, &buf TSRMLS_CC) != SUCCESS) {
1199                return FAILURE;
1200            }
1201        }
1202
1203        delta = XG(request_time) - buf.st_mtime;
1204        if (abs(delta) < 2 && !xc_test) {
1205            return FAILURE;
1206        }
1207
1208        compiler->new_entry.file_mtime   = buf.st_mtime;
1209        compiler->new_entry.file_size    = buf.st_size;
1210        compiler->new_entry.file_device  = buf.st_dev;
1211        compiler->new_entry.file_inode   = buf.st_ino;
1212    }
1213    else {
1214        xc_entry_php_quick_resolve_opened_path(compiler, NULL TSRMLS_CC);
1215
1216        compiler->new_entry.file_mtime   = 0;
1217        compiler->new_entry.file_size    = 0;
1218        compiler->new_entry.file_device  = 0;
1219        compiler->new_entry.file_inode   = 0;
1220    }
1221
1222    {
1223        xc_hash_value_t basename_hash_value;
1224        if (xc_php_hcache.size > 1
1225         || !compiler->new_entry.file_inode) {
1226            const char *filename_end = compiler->filename + compiler->filename_len;
1227            const char *basename_begin = filename_end - 1;
1228
1229            /* scan till out of basename part */
1230            while (basename_begin >= compiler->filename && !IS_SLASH(*basename_begin)) {
1231                --basename_begin;
1232            }
1233            /* get back to basename_begin */
1234            ++basename_begin;
1235
1236            basename_hash_value = HASH_STR_L(basename_begin, filename_end - basename_begin);
1237        }
1238
1239        compiler->entry_hash.cacheid = xc_php_hcache.size > 1 ? xc_hash_fold(basename_hash_value, &xc_php_hcache) : 0;
1240        compiler->entry_hash.entryslotid = xc_hash_fold(
1241                compiler->new_entry.file_inode
1242                ? (xc_hash_value_t) HASH(compiler->new_entry.file_device + compiler->new_entry.file_inode)
1243                : basename_hash_value
1244                , &xc_php_hentry);
1245    }
1246
1247    compiler->new_entry.filepath  = NULL;
1248    compiler->new_entry.dirpath   = NULL;
1249#ifdef IS_UNICODE
1250    compiler->new_entry.ufilepath = NULL;
1251    compiler->new_entry.udirpath  = NULL;
1252#endif
1253
1254    return SUCCESS;
1255}
1256/* }}} */
1257static inline xc_hash_value_t xc_php_hash_md5(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1258{
1259    return HASH_STR_S(php->md5.digest, sizeof(php->md5.digest));
1260}
1261/* }}} */
1262static int xc_entry_data_php_init_md5(xc_cache_t *cache, xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
1263{
1264    unsigned char   buf[1024];
1265    PHP_MD5_CTX     context;
1266    int             n;
1267    php_stream     *stream;
1268    ulong           old_rsid = EG(regular_list).nNextFreeElement;
1269
1270    stream = php_stream_open_wrapper((char *) compiler->filename, "rb", USE_PATH | REPORT_ERRORS | ENFORCE_SAFE_MODE | STREAM_DISABLE_OPEN_BASEDIR, NULL);
1271    if (!stream) {
1272        return FAILURE;
1273    }
1274
1275    PHP_MD5Init(&context);
1276    while ((n = php_stream_read(stream, (char *) buf, sizeof(buf))) > 0) {
1277        PHP_MD5Update(&context, buf, n);
1278    }
1279    PHP_MD5Final((unsigned char *) compiler->new_php.md5.digest, &context);
1280
1281    php_stream_close(stream);
1282    if (EG(regular_list).nNextFreeElement == old_rsid + 1) {
1283        EG(regular_list).nNextFreeElement = old_rsid;
1284    }
1285
1286    if (n < 0) {
1287        return FAILURE;
1288    }
1289
1290    compiler->new_php.hvalue = (xc_php_hash_md5(&compiler->new_php TSRMLS_CC) & cache->hphp->mask);
1291#ifdef XCACHE_DEBUG
1292    {
1293        char md5str[33];
1294        make_digest(md5str, (unsigned char *) compiler->new_php.md5.digest);
1295        TRACE("md5 %s", md5str);
1296    }
1297#endif
1298
1299    return SUCCESS;
1300}
1301/* }}} */
1302static void xc_entry_php_init(xc_entry_php_t *entry_php, const char *filepath TSRMLS_DC) /* {{{*/
1303{
1304    entry_php->filepath     = ZEND_24((char *), NOTHING) filepath;
1305    entry_php->filepath_len = strlen(entry_php->filepath);
1306    entry_php->dirpath      = estrndup(entry_php->filepath, entry_php->filepath_len);
1307    entry_php->dirpath_len  = zend_dirname(entry_php->dirpath, entry_php->filepath_len);
1308#ifdef IS_UNICODE
1309    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);
1310    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);
1311#endif
1312}
1313/* }}} */
1314#ifndef ZEND_COMPILE_DELAYED_BINDING
1315static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
1316{
1317    char *class_name;
1318    zend_uint i;
1319    int class_len;
1320    xc_cest_t cest;
1321    xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
1322
1323    class_name = Z_OP_CONSTANT(opline->op1).value.str.val;
1324    class_len  = Z_OP_CONSTANT(opline->op1).value.str.len;
1325    if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
1326        assert(0);
1327    }
1328    TRACE("got ZEND_DECLARE_INHERITED_CLASS: %s", class_name + 1);
1329    /* let's see which class */
1330    for (i = 0; i < php->classinfo_cnt; i ++) {
1331        if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
1332            php->classinfos[i].oplineno = oplineno;
1333            php->have_early_binding = 1;
1334            break;
1335        }
1336    }
1337
1338    if (i == php->classinfo_cnt) {
1339        assert(0);
1340    }
1341}
1342/* }}} */
1343#endif
1344
1345/* {{{ Constant Usage */
1346#ifdef ZEND_ENGINE_2_4
1347#   define xcache_literal_is_dir  1
1348#   define xcache_literal_is_file 2
1349#else
1350#   define xcache_op1_is_file 1
1351#   define xcache_op1_is_dir  2
1352#   define xcache_op2_is_file 4
1353#   define xcache_op2_is_dir  8
1354#endif
1355typedef struct {
1356    zend_bool filepath_used;
1357    zend_bool dirpath_used;
1358    zend_bool ufilepath_used;
1359    zend_bool udirpath_used;
1360} xc_const_usage_t;
1361/* }}} */
1362static 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) /* {{{ */
1363{
1364#ifdef ZEND_ENGINE_2_4
1365    int literalindex;
1366#else
1367    zend_uint oplinenum;
1368#endif
1369    xc_vector_t details;
1370
1371    xc_vector_init(xc_op_array_info_detail_t, &details);
1372
1373#define XCACHE_ANALYZE_LITERAL(type) \
1374    if (zend_binary_strcmp(Z_STRVAL(literal->constant), Z_STRLEN(literal->constant), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
1375        usage->type##path_used = 1; \
1376        literalinfo |= xcache_##literal##_is_##type; \
1377    }
1378
1379#define XCACHE_U_ANALYZE_LITERAL(type) \
1380    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) { \
1381        usage->u##type##path_used = 1; \
1382        literalinfo |= xcache_##literal##_is_##type; \
1383    }
1384
1385#define XCACHE_ANALYZE_OP(type, op) \
1386    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) { \
1387        usage->type##path_used = 1; \
1388        oplineinfo |= xcache_##op##_is_##type; \
1389    }
1390
1391#define XCACHE_U_ANALYZE_OP(type, op) \
1392    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) { \
1393        usage->u##type##path_used = 1; \
1394        oplineinfo |= xcache_##op##_is_##type; \
1395    }
1396
1397#ifdef ZEND_ENGINE_2_4
1398    for (literalindex = 0; literalindex < op_array->last_literal; literalindex++) {
1399        zend_literal *literal = &op_array->literals[literalindex];
1400        zend_uint literalinfo = 0;
1401        if (Z_TYPE(literal->constant) == IS_STRING) {
1402            XCACHE_ANALYZE_LITERAL(file)
1403            else XCACHE_ANALYZE_LITERAL(dir)
1404        }
1405#ifdef IS_UNICODE
1406        else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1407            XCACHE_U_ANALYZE_LITERAL(file)
1408            else XCACHE_U_ANALYZE_LITERAL(dir)
1409        }
1410#endif
1411        if (literalinfo) {
1412            xc_op_array_info_detail_t detail;
1413            detail.index = literalindex;
1414            detail.info  = literalinfo;
1415            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1416        }
1417    }
1418
1419    op_array_info->literalinfo_cnt = details.cnt;
1420    op_array_info->literalinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1421#else /* ZEND_ENGINE_2_4 */
1422    for (oplinenum = 0; oplinenum < op_array->last; oplinenum++) {
1423        zend_op *opline = &op_array->opcodes[oplinenum];
1424        zend_uint oplineinfo = 0;
1425        if (Z_OP_TYPE(opline->op1) == IS_CONST) {
1426            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1427                XCACHE_ANALYZE_OP(file, op1)
1428                else XCACHE_ANALYZE_OP(dir, op1)
1429            }
1430#ifdef IS_UNICODE
1431            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1432                XCACHE_U_ANALYZE_OP(file, op1)
1433                else XCACHE_U_ANALYZE_OP(dir, op1)
1434            }
1435#endif
1436        }
1437
1438        if (Z_OP_TYPE(opline->op2) == IS_CONST) {
1439            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1440                XCACHE_ANALYZE_OP(file, op2)
1441                else XCACHE_ANALYZE_OP(dir, op2)
1442            }
1443#ifdef IS_UNICODE
1444            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1445                XCACHE_U_ANALYZE_OP(file, op2)
1446                else XCACHE_U_ANALYZE_OP(dir, op2)
1447            }
1448#endif
1449        }
1450
1451        if (oplineinfo) {
1452            xc_op_array_info_detail_t detail;
1453            detail.index = oplinenum;
1454            detail.info  = oplineinfo;
1455            xc_vector_add(xc_op_array_info_detail_t, &details, detail);
1456        }
1457    }
1458
1459    op_array_info->oplineinfo_cnt = details.cnt;
1460    op_array_info->oplineinfos    = xc_vector_detach(xc_op_array_info_detail_t, &details);
1461#endif /* ZEND_ENGINE_2_4 */
1462    xc_vector_free(xc_op_array_info_detail_t, &details);
1463}
1464/* }}} */
1465void 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) /* {{{ */
1466{
1467#ifdef ZEND_ENGINE_2_4
1468    zend_uint literalinfoindex;
1469
1470    for (literalinfoindex = 0; literalinfoindex < op_array_info->literalinfo_cnt; ++literalinfoindex) {
1471        int literalindex = op_array_info->literalinfos[literalinfoindex].index;
1472        int literalinfo = op_array_info->literalinfos[literalinfoindex].info;
1473        zend_literal *literal = &op_array->literals[literalindex];
1474        if ((literalinfo & xcache_literal_is_file)) {
1475            if (!shallow_copy) {
1476                efree(Z_STRVAL(literal->constant));
1477            }
1478            if (Z_TYPE(literal->constant) == IS_STRING) {
1479                assert(entry_php->filepath);
1480                ZVAL_STRINGL(&literal->constant, entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1481                TRACE("restored literal constant: %s", entry_php->filepath);
1482            }
1483#ifdef IS_UNICODE
1484            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1485                assert(entry_php->ufilepath);
1486                ZVAL_UNICODEL(&literal->constant, entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1487            }
1488#endif
1489            else {
1490                assert(0);
1491            }
1492        }
1493        else if ((literalinfo & xcache_literal_is_dir)) {
1494            if (!shallow_copy) {
1495                efree(Z_STRVAL(literal->constant));
1496            }
1497            if (Z_TYPE(literal->constant) == IS_STRING) {
1498                assert(entry_php->dirpath);
1499                TRACE("restored literal constant: %s", entry_php->dirpath);
1500                ZVAL_STRINGL(&literal->constant, entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1501            }
1502#ifdef IS_UNICODE
1503            else if (Z_TYPE(literal->constant) == IS_UNICODE) {
1504                assert(!entry_php->udirpath);
1505                ZVAL_UNICODEL(&literal->constant, entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1506            }
1507#endif
1508            else {
1509                assert(0);
1510            }
1511        }
1512    }
1513#else /* ZEND_ENGINE_2_4 */
1514    zend_uint oplinenum;
1515
1516    for (oplinenum = 0; oplinenum < op_array_info->oplineinfo_cnt; ++oplinenum) {
1517        int oplineindex = op_array_info->oplineinfos[oplinenum].index;
1518        int oplineinfo = op_array_info->oplineinfos[oplinenum].info;
1519        zend_op *opline = &op_array->opcodes[oplineindex];
1520        if ((oplineinfo & xcache_op1_is_file)) {
1521            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1522            if (!shallow_copy) {
1523                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1524            }
1525            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1526                assert(entry_php->filepath);
1527                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1528                TRACE("restored op1 constant: %s", entry_php->filepath);
1529            }
1530#ifdef IS_UNICODE
1531            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1532                assert(entry_php->ufilepath);
1533                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1534            }
1535#endif
1536            else {
1537                assert(0);
1538            }
1539        }
1540        else if ((oplineinfo & xcache_op1_is_dir)) {
1541            assert(Z_OP_TYPE(opline->op1) == IS_CONST);
1542            if (!shallow_copy) {
1543                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
1544            }
1545            if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
1546                assert(entry_php->dirpath);
1547                TRACE("restored op1 constant: %s", entry_php->dirpath);
1548                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1549            }
1550#ifdef IS_UNICODE
1551            else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
1552                assert(!entry_php->udirpath);
1553                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1554            }
1555#endif
1556            else {
1557                assert(0);
1558            }
1559        }
1560
1561        if ((oplineinfo & xcache_op2_is_file)) {
1562            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1563            if (!shallow_copy) {
1564                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1565            }
1566            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1567                assert(entry_php->filepath);
1568                TRACE("restored op2 constant: %s", entry_php->filepath);
1569                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
1570            }
1571#ifdef IS_UNICODE
1572            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1573                assert(entry_php->ufilepath);
1574                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
1575            }
1576#endif
1577            else {
1578                assert(0);
1579            }
1580        }
1581        else if ((oplineinfo & xcache_op2_is_dir)) {
1582            assert(Z_OP_TYPE(opline->op2) == IS_CONST);
1583            if (!shallow_copy) {
1584                efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
1585            }
1586            if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
1587                assert(entry_php->dirpath);
1588                TRACE("restored op2 constant: %s", entry_php->dirpath);
1589                ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
1590            }
1591#ifdef IS_UNICODE
1592            else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
1593                assert(entry_php->udirpath);
1594                ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
1595            }
1596#endif
1597            else {
1598                assert(0);
1599            }
1600        }
1601    }
1602#endif /* ZEND_ENGINE_2_4 */
1603}
1604/* }}} */
1605static void xc_free_op_array_info(xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
1606{
1607#ifdef ZEND_ENGINE_2_4
1608    if (op_array_info->literalinfos) {
1609        efree(op_array_info->literalinfos);
1610    }
1611#else
1612    if (op_array_info->oplineinfos) {
1613        efree(op_array_info->oplineinfos);
1614    }
1615#endif
1616}
1617/* }}} */
1618static void xc_free_php(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
1619{
1620    zend_uint i;
1621    if (php->classinfos) {
1622        for (i = 0; i < php->classinfo_cnt; i ++) {
1623            xc_classinfo_t *classinfo = &php->classinfos[i];
1624            zend_uint j;
1625
1626            for (j = 0; j < classinfo->methodinfo_cnt; j ++) {
1627                xc_free_op_array_info(&classinfo->methodinfos[j] TSRMLS_CC);
1628            }
1629
1630            if (classinfo->methodinfos) {
1631                efree(classinfo->methodinfos);
1632            }
1633        }
1634    }
1635    if (php->funcinfos) {
1636        for (i = 0; i < php->funcinfo_cnt; i ++) {
1637            xc_free_op_array_info(&php->funcinfos[i].op_array_info TSRMLS_CC);
1638        }
1639    }
1640    xc_free_op_array_info(&php->op_array_info TSRMLS_CC);
1641
1642#define X_FREE(var) do {\
1643    if (php->var) { \
1644        efree(php->var); \
1645    } \
1646} while (0)
1647
1648#ifdef ZEND_ENGINE_2_1
1649    X_FREE(autoglobals);
1650#endif
1651    X_FREE(classinfos);
1652    X_FREE(funcinfos);
1653#ifdef HAVE_XCACHE_CONSTANT
1654    X_FREE(constinfos);
1655#endif
1656#undef X_FREE
1657}
1658/* }}} */
1659static void xc_compile_php(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
1660{
1661    zend_uint old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
1662    zend_bool catched = 0;
1663
1664    /* {{{ compile */
1665    TRACE("compiling %s", h->opened_path ? h->opened_path : h->filename);
1666
1667    old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
1668    old_funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
1669    old_constinfo_cnt = zend_hash_num_elements(EG(zend_constants));
1670
1671    zend_try {
1672        compiler->new_php.op_array = old_compile_file(h, type TSRMLS_CC);
1673    } zend_catch {
1674        catched = 1;
1675    } zend_end_try();
1676
1677    if (catched) {
1678        goto err_bailout;
1679    }
1680
1681    if (compiler->new_php.op_array == NULL) {
1682        goto err_op_array;
1683    }
1684
1685    if (!XG(initial_compile_file_called)) {
1686        TRACE("%s", "!initial_compile_file_called, give up");
1687        return;
1688    }
1689
1690    /* }}} */
1691    /* {{{ prepare */
1692    zend_restore_compiled_filename(h->opened_path ? h->opened_path : (char *) h->filename TSRMLS_CC);
1693
1694#ifdef HAVE_XCACHE_CONSTANT
1695    compiler->new_php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
1696#endif
1697    compiler->new_php.funcinfo_cnt   = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
1698    compiler->new_php.classinfo_cnt  = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
1699#ifdef ZEND_ENGINE_2_1
1700    /* {{{ count new_php.autoglobal_cnt */ {
1701        Bucket *b;
1702
1703        compiler->new_php.autoglobal_cnt = 0;
1704        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1705            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1706            /* check if actived */
1707            if (auto_global->auto_global_callback && !auto_global->armed) {
1708                compiler->new_php.autoglobal_cnt ++;
1709            }
1710        }
1711    }
1712    /* }}} */
1713#endif
1714
1715#define X_ALLOC_N(var, cnt) do {     \
1716    if (compiler->new_php.cnt) {                  \
1717        ECALLOC_N(compiler->new_php.var, compiler->new_php.cnt); \
1718        if (!compiler->new_php.var) {             \
1719            goto err_alloc;          \
1720        }                            \
1721    }                                \
1722    else {                           \
1723        compiler->new_php.var = NULL;             \
1724    }                                \
1725} while (0)
1726
1727#ifdef HAVE_XCACHE_CONSTANT
1728    X_ALLOC_N(constinfos,  constinfo_cnt);
1729#endif
1730    X_ALLOC_N(funcinfos,   funcinfo_cnt);
1731    X_ALLOC_N(classinfos,  classinfo_cnt);
1732#ifdef ZEND_ENGINE_2_1
1733    X_ALLOC_N(autoglobals, autoglobal_cnt);
1734#endif
1735#undef X_ALLOC
1736    /* }}} */
1737
1738    /* {{{ shallow copy, pointers only */ {
1739        Bucket *b;
1740        zend_uint i;
1741        zend_uint j;
1742
1743#define COPY_H(vartype, var, cnt, name, datatype) do {        \
1744    for (i = 0, j = 0; b; i ++, b = b->pListNext) {           \
1745        vartype *data = &compiler->new_php.var[j];                         \
1746                                                              \
1747        if (i < old_##cnt) {                                  \
1748            continue;                                         \
1749        }                                                     \
1750        j ++;                                                 \
1751                                                              \
1752        assert(i < old_##cnt + compiler->new_php.cnt);                     \
1753        assert(b->pData);                                     \
1754        memcpy(&data->name, b->pData, sizeof(datatype));      \
1755        UNISW(NOTHING, data->type = b->key.type;)             \
1756        if (UNISW(1, b->key.type == IS_STRING)) {             \
1757            ZSTR_S(data->key)      = BUCKET_KEY_S(b);         \
1758        }                                                     \
1759        else {                                                \
1760            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
1761        }                                                     \
1762        data->key_size   = b->nKeyLength;                     \
1763        data->h          = b->h;                              \
1764    }                                                         \
1765} while(0)
1766
1767#ifdef HAVE_XCACHE_CONSTANT
1768        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1769#endif
1770        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1771        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1772
1773#undef COPY_H
1774
1775        /* for ZE1, cest need to be fixed inside store */
1776
1777#ifdef ZEND_ENGINE_2_1
1778        /* scan for acatived auto globals */
1779        i = 0;
1780        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1781            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1782            /* check if actived */
1783            if (auto_global->auto_global_callback && !auto_global->armed) {
1784                xc_autoglobal_t *data = &compiler->new_php.autoglobals[i];
1785
1786                assert(i < compiler->new_php.autoglobal_cnt);
1787                i ++;
1788                UNISW(NOTHING, data->type = b->key.type;)
1789                if (UNISW(1, b->key.type == IS_STRING)) {
1790                    ZSTR_S(data->key)     = BUCKET_KEY_S(b);
1791                }
1792                else {
1793                    ZSTR_U(data->key)     = BUCKET_KEY_U(b);
1794                }
1795                data->key_len = b->nKeyLength - 1;
1796                data->h       = b->h;
1797            }
1798        }
1799#endif
1800    }
1801    /* }}} */
1802
1803    /* {{{ collect info for file/dir path */ {
1804        Bucket *b;
1805        xc_const_usage_t const_usage;
1806        unsigned int i;
1807
1808        xc_entry_php_init(&compiler->new_entry, zend_get_compiled_filename(TSRMLS_C) TSRMLS_CC);
1809        memset(&const_usage, 0, sizeof(const_usage));
1810
1811        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1812            xc_classinfo_t *classinfo = &compiler->new_php.classinfos[i];
1813            zend_class_entry *ce = CestToCePtr(classinfo->cest);
1814            classinfo->methodinfo_cnt = ce->function_table.nTableSize;
1815            if (classinfo->methodinfo_cnt) {
1816                int j;
1817
1818                ECALLOC_N(classinfo->methodinfos, classinfo->methodinfo_cnt);
1819                if (!classinfo->methodinfos) {
1820                    goto err_alloc;
1821                }
1822
1823                for (j = 0, b = ce->function_table.pListHead; b; j ++, b = b->pListNext) {
1824                    xc_collect_op_array_info(compiler, &const_usage, &classinfo->methodinfos[j], (zend_op_array *) b->pData TSRMLS_CC);
1825                }
1826            }
1827            else {
1828                classinfo->methodinfos = NULL;
1829            }
1830        }
1831
1832        for (i = 0; i < compiler->new_php.funcinfo_cnt; i ++) {
1833            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);
1834        }
1835
1836        xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.op_array_info, compiler->new_php.op_array TSRMLS_CC);
1837
1838        /* file/dir path free unused */
1839#define X_FREE_UNUSED(var) \
1840        if (!const_usage.var##path_used) { \
1841            efree(compiler->new_entry.var##path); \
1842            compiler->new_entry.var##path = NULL; \
1843            compiler->new_entry.var##path_len = 0; \
1844        }
1845        /* filepath is required to restore op_array->filename, so no free filepath here */
1846        X_FREE_UNUSED(dir)
1847#ifdef IS_UNICODE
1848        X_FREE_UNUSED(ufile)
1849        X_FREE_UNUSED(udir)
1850#endif
1851#undef X_FREE_UNUSED
1852    }
1853    /* }}} */
1854#ifdef XCACHE_ERROR_CACHING
1855    compiler->new_php.compilererrors = xc_sandbox_compilererrors(TSRMLS_C);
1856    compiler->new_php.compilererror_cnt = xc_sandbox_compilererror_cnt(TSRMLS_C);
1857#endif
1858#ifndef ZEND_COMPILE_DELAYED_BINDING
1859    /* {{{ find inherited classes that should be early-binding */
1860    compiler->new_php.have_early_binding = 0;
1861    {
1862        zend_uint i;
1863        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1864            compiler->new_php.classinfos[i].oplineno = -1;
1865        }
1866    }
1867
1868    xc_undo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1869    xc_foreach_early_binding_class(compiler->new_php.op_array, xc_cache_early_binding_class_cb, (void *) &compiler->new_php TSRMLS_CC);
1870    xc_redo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1871    /* }}} */
1872#endif
1873
1874    return;
1875
1876err_alloc:
1877    xc_free_php(&compiler->new_php TSRMLS_CC);
1878
1879err_bailout:
1880err_op_array:
1881
1882    if (catched) {
1883        zend_bailout();
1884    }
1885}
1886/* }}} */
1887static zend_op_array *xc_compile_restore(xc_entry_php_t *stored_entry, xc_entry_data_php_t *stored_php TSRMLS_DC) /* {{{ */
1888{
1889    zend_op_array *op_array;
1890    xc_entry_php_t restored_entry;
1891    xc_entry_data_php_t restored_php;
1892    zend_bool catched;
1893    zend_uint i;
1894
1895    /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
1896    i = 1;
1897    zend_hash_add(&EG(included_files), stored_entry->entry.name.str.val, stored_entry->entry.name.str.len + 1, (void *)&i, sizeof(int), NULL);
1898
1899    CG(in_compilation)    = 1;
1900    CG(compiled_filename) = stored_entry->entry.name.str.val;
1901    CG(zend_lineno)       = 0;
1902    TRACE("restoring %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1903    xc_processor_restore_xc_entry_php_t(&restored_entry, stored_entry TSRMLS_CC);
1904    xc_processor_restore_xc_entry_data_php_t(stored_entry, &restored_php, stored_php, xc_readonly_protection TSRMLS_CC);
1905    restored_entry.php = &restored_php;
1906#ifdef SHOW_DPRINT
1907    xc_dprint(&restored_entry, 0 TSRMLS_CC);
1908#endif
1909
1910    catched = 0;
1911    zend_try {
1912        op_array = xc_entry_install(&restored_entry TSRMLS_CC);
1913    } zend_catch {
1914        catched = 1;
1915    } zend_end_try();
1916
1917#ifdef HAVE_XCACHE_CONSTANT
1918    if (restored_php.constinfos) {
1919        efree(restored_php.constinfos);
1920    }
1921#endif
1922    if (restored_php.funcinfos) {
1923        efree(restored_php.funcinfos);
1924    }
1925    if (restored_php.classinfos) {
1926        efree(restored_php.classinfos);
1927    }
1928
1929    if (catched) {
1930        zend_bailout();
1931    }
1932    CG(in_compilation)    = 0;
1933    CG(compiled_filename) = NULL;
1934    TRACE("restored %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1935    return op_array;
1936}
1937/* }}} */
1938typedef struct xc_sandboxed_compiler_t { /* {{{ */
1939    xc_compiler_t *compiler;
1940    /* input */
1941    zend_file_handle *h;
1942    int type;
1943
1944    /* sandbox output */
1945    xc_entry_php_t *stored_entry;
1946    xc_entry_data_php_t *stored_php;
1947} xc_sandboxed_compiler_t;
1948/* }}} */
1949
1950static zend_op_array *xc_compile_file_sandboxed(void *data TSRMLS_DC) /* {{{ */
1951{
1952    xc_sandboxed_compiler_t *sandboxed_compiler = (xc_sandboxed_compiler_t *) data;
1953    xc_compiler_t *compiler = sandboxed_compiler->compiler;
1954    zend_bool catched = 0;
1955    xc_cache_t *cache = &xc_php_caches[compiler->entry_hash.cacheid];
1956    xc_entry_php_t *stored_entry;
1957    xc_entry_data_php_t *stored_php;
1958
1959    /* {{{ compile */
1960    /* make compile inside sandbox */
1961#ifdef HAVE_XCACHE_CONSTANT
1962    compiler->new_php.constinfos  = NULL;
1963#endif
1964    compiler->new_php.funcinfos   = NULL;
1965    compiler->new_php.classinfos  = NULL;
1966#ifdef ZEND_ENGINE_2_1
1967    compiler->new_php.autoglobals = NULL;
1968#endif
1969    memset(&compiler->new_php.op_array_info, 0, sizeof(compiler->new_php.op_array_info));
1970
1971    XG(initial_compile_file_called) = 0;
1972    zend_try {
1973        compiler->new_php.op_array = NULL;
1974        xc_compile_php(compiler, sandboxed_compiler->h, sandboxed_compiler->type TSRMLS_CC);
1975    } zend_catch {
1976        catched = 1;
1977    } zend_end_try();
1978
1979    if (catched
1980     || !compiler->new_php.op_array /* possible ? */
1981     || !XG(initial_compile_file_called)) {
1982        goto err_aftersandbox;
1983    }
1984
1985    /* }}} */
1986#ifdef SHOW_DPRINT
1987    compiler->new_entry.php = &compiler->new_php;
1988    xc_dprint(&compiler->new_entry, 0 TSRMLS_CC);
1989#endif
1990
1991    stored_entry = NULL;
1992    stored_php = NULL;
1993    ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
1994        /* php_store */
1995        stored_php = xc_php_store_unlocked(cache, &compiler->new_php TSRMLS_CC);
1996        if (!stored_php) {
1997            /* error */
1998            break;
1999        }
2000        /* entry_store */
2001        compiler->new_entry.php = stored_php;
2002        stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
2003        if (stored_entry) {
2004            xc_php_addref_unlocked(stored_php);
2005            TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2006            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2007        }
2008    } LEAVE_LOCK_EX(cache);
2009    /* }}} */
2010    TRACE("%s", stored_entry ? "stored" : "store failed");
2011
2012    if (catched || !stored_php) {
2013        goto err_aftersandbox;
2014    }
2015
2016    cache->cached->compiling = 0;
2017    xc_free_php(&compiler->new_php TSRMLS_CC);
2018
2019    if (stored_entry) {
2020        sandboxed_compiler->stored_entry = stored_entry;
2021        sandboxed_compiler->stored_php = stored_php;
2022        /* discard newly compiled result, restore from stored one */
2023        if (compiler->new_php.op_array) {
2024#ifdef ZEND_ENGINE_2
2025            destroy_op_array(compiler->new_php.op_array TSRMLS_CC);
2026#else
2027            destroy_op_array(compiler->new_php.op_array);
2028#endif
2029            efree(compiler->new_php.op_array);
2030            compiler->new_php.op_array = NULL;
2031        }
2032        return NULL;
2033    }
2034    else {
2035        return compiler->new_php.op_array;
2036    }
2037
2038err_aftersandbox:
2039    xc_free_php(&compiler->new_php TSRMLS_CC);
2040
2041    cache->cached->compiling = 0;
2042    if (catched) {
2043        cache->cached->errors ++;
2044        zend_bailout();
2045    }
2046    return compiler->new_php.op_array;
2047} /* }}} */
2048static zend_op_array *xc_compile_file_cached(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2049{
2050    /*
2051    if (clog) {
2052        return old;
2053    }
2054
2055    if (cached_entry = getby entry_hash) {
2056        php = cached_entry.php;
2057        php = restore(php);
2058        return php;
2059    }
2060    else {
2061        if (!(php = getby md5)) {
2062            if (clog) {
2063                return old;
2064            }
2065
2066            inside_sandbox {
2067                php = compile;
2068                entry = create entries[entry];
2069            }
2070        }
2071
2072        entry.php = php;
2073        return php;
2074    }
2075    */
2076
2077    xc_entry_php_t *stored_entry;
2078    xc_entry_data_php_t *stored_php;
2079    zend_bool gaveup = 0;
2080    zend_bool catched = 0;
2081    zend_op_array *op_array;
2082    xc_cache_t *cache = &xc_php_caches[compiler->entry_hash.cacheid];
2083    xc_sandboxed_compiler_t sandboxed_compiler;
2084
2085    if (cache->cached->disabled) {
2086        return old_compile_file(h, type TSRMLS_CC);
2087    }
2088    /* stale skips precheck */
2089    if (cache->cached->disabled || XG(request_time) - cache->cached->compiling < 30) {
2090        cache->cached->skips ++;
2091        return old_compile_file(h, type TSRMLS_CC);
2092    }
2093
2094    /* {{{ entry_lookup/hit/md5_init/php_lookup */
2095    stored_entry = NULL;
2096    stored_php = NULL;
2097
2098    ENTER_LOCK_EX(cache) {
2099        if (!compiler->opened_path && xc_resolve_path_check_entry_unlocked(compiler, compiler->filename, &stored_entry TSRMLS_CC) == SUCCESS) {
2100            compiler->opened_path = compiler->new_entry.entry.name.str.val;
2101        }
2102        else {
2103            if (!compiler->opened_path && xc_entry_php_resolve_opened_path(compiler, NULL TSRMLS_CC) != SUCCESS) {
2104                gaveup = 1;
2105                break;
2106            }
2107
2108            /* finalize name */
2109            compiler->new_entry.entry.name.str.val = (char *) compiler->opened_path;
2110            compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
2111
2112            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);
2113        }
2114
2115        if (stored_entry) {
2116            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2117
2118            TRACE(" hit %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2119            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2120            stored_php = stored_entry->php;
2121            break;
2122        }
2123
2124        TRACE("miss entry %d:%s", compiler->new_entry.file_inode, compiler->new_entry.entry.name.str.val);
2125
2126        if (xc_entry_data_php_init_md5(cache, compiler TSRMLS_CC) != SUCCESS) {
2127            gaveup = 1;
2128            break;
2129        }
2130
2131        stored_php = xc_php_find_unlocked(cache->cached, &compiler->new_php TSRMLS_CC);
2132
2133        if (stored_php) {
2134            compiler->new_entry.php = stored_php;
2135            xc_entry_php_init(&compiler->new_entry, compiler->opened_path TSRMLS_CC);
2136            stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
2137            if (stored_entry) {
2138                xc_php_addref_unlocked(stored_php);
2139                TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2140                xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2141            }
2142            else {
2143                gaveup = 1;
2144            }
2145            break;
2146        }
2147
2148        if (XG(request_time) - cache->cached->compiling < 30) {
2149            TRACE("%s", "miss php, but compiling");
2150            cache->cached->skips ++;
2151            gaveup = 1;
2152            break;
2153        }
2154
2155        TRACE("%s", "miss php, going to compile");
2156        cache->cached->compiling = XG(request_time);
2157    } LEAVE_LOCK_EX(cache);
2158
2159    if (catched) {
2160        cache->cached->compiling = 0;
2161        zend_bailout();
2162    }
2163
2164    /* found entry */
2165    if (stored_entry && stored_php) {
2166        zend_llist_add_element(&CG(open_files), h);
2167        return xc_compile_restore(stored_entry, stored_php TSRMLS_CC);
2168    }
2169
2170    /* gaveup */
2171    if (gaveup) {
2172        return old_compile_file(h, type TSRMLS_CC);
2173    }
2174    /* }}} */
2175
2176    sandboxed_compiler.compiler = compiler;
2177    sandboxed_compiler.h = h;
2178    sandboxed_compiler.type = type;
2179    sandboxed_compiler.stored_php = NULL;
2180    sandboxed_compiler.stored_entry = NULL;
2181    op_array = xc_sandbox(xc_compile_file_sandboxed, (void *) &sandboxed_compiler, h->opened_path ? h->opened_path : h->filename TSRMLS_CC);
2182    if (sandboxed_compiler.stored_entry) {
2183        return xc_compile_restore(sandboxed_compiler.stored_entry, sandboxed_compiler.stored_php TSRMLS_CC);
2184    }
2185    else {
2186        return op_array;
2187    }
2188}
2189/* }}} */
2190static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2191{
2192    xc_compiler_t compiler;
2193    zend_op_array *op_array;
2194
2195    assert(xc_initized);
2196
2197    TRACE("xc_compile_file: type=%d name=%s", h->type, h->filename ? h->filename : "NULL");
2198
2199    if (!XG(cacher)
2200     || !h->filename
2201     || !SG(request_info).path_translated
2202
2203    ) {
2204        TRACE("%s", "cacher not enabled");
2205        return old_compile_file(h, type TSRMLS_CC);
2206    }
2207
2208    /* {{{ entry_init_key */
2209    compiler.opened_path = h->opened_path;
2210    compiler.filename = compiler.opened_path ? compiler.opened_path : h->filename;
2211    compiler.filename_len = strlen(compiler.filename);
2212    if (xc_entry_php_init_key(&compiler TSRMLS_CC) != SUCCESS) {
2213        TRACE("failed to init key for %s", compiler.filename);
2214        return old_compile_file(h, type TSRMLS_CC);
2215    }
2216    /* }}} */
2217
2218    op_array = xc_compile_file_cached(&compiler, h, type TSRMLS_CC);
2219
2220    xc_entry_free_key_php(&compiler.new_entry TSRMLS_CC);
2221
2222    return op_array;
2223}
2224/* }}} */
2225
2226/* gdb helper functions, but N/A for coredump */
2227int xc_is_rw(const void *p) /* {{{ */
2228{
2229    xc_shm_t *shm;
2230    size_t i;
2231
2232    if (xc_php_caches) {
2233        for (i = 0; i < xc_php_hcache.size; i ++) {
2234            shm = xc_php_caches[i].shm;
2235            if (shm->handlers->is_readwrite(shm, p)) {
2236                return 1;
2237            }
2238        }
2239    }
2240
2241    if (xc_var_caches) {
2242        for (i = 0; i < xc_var_hcache.size; i ++) {
2243            shm = xc_var_caches[i].shm;
2244            if (shm->handlers->is_readwrite(shm, p)) {
2245                return 1;
2246            }
2247        }
2248    }
2249    return 0;
2250}
2251/* }}} */
2252int xc_is_ro(const void *p) /* {{{ */
2253{
2254    xc_shm_t *shm;
2255    size_t i;
2256
2257    if (xc_php_caches) {
2258        for (i = 0; i < xc_php_hcache.size; i ++) {
2259            shm = xc_php_caches[i].shm;
2260            if (shm->handlers->is_readonly(shm, p)) {
2261                return 1;
2262            }
2263        }
2264    }
2265
2266    if (xc_var_caches) {
2267        for (i = 0; i < xc_var_hcache.size; i ++) {
2268            shm = xc_var_caches[i].shm;
2269            if (shm->handlers->is_readonly(shm, p)) {
2270                return 1;
2271            }
2272        }
2273    }
2274    return 0;
2275}
2276/* }}} */
2277int xc_is_shm(const void *p) /* {{{ */
2278{
2279    return xc_is_ro(p) || xc_is_rw(p);
2280}
2281/* }}} */
2282
2283void xc_gc_add_op_array(xc_gc_op_array_t *gc_op_array TSRMLS_DC) /* {{{ */
2284{
2285    zend_llist_add_element(&XG(gc_op_arrays), (void *) gc_op_array);
2286}
2287/* }}} */
2288static void xc_gc_op_array(void *pDest) /* {{{ */
2289{
2290    xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
2291    zend_uint i;
2292#ifdef ZEND_ENGINE_2
2293    if (op_array->arg_info) {
2294        for (i = 0; i < op_array->num_args; i++) {
2295            efree((char *) ZSTR_V(op_array->arg_info[i].name));
2296            if (ZSTR_V(op_array->arg_info[i].class_name)) {
2297                efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
2298            }
2299        }
2300        efree(op_array->arg_info);
2301    }
2302#endif
2303    if (op_array->opcodes) {
2304        efree(op_array->opcodes);
2305    }
2306}
2307/* }}} */
2308
2309/* module helper function */
2310static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
2311{
2312    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2313    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2314    return 0;
2315}
2316/* }}} */
2317static xc_shm_t *xc_cache_destroy(xc_cache_t *caches, xc_hash_t *hcache) /* {{{ */
2318{
2319    size_t i;
2320    xc_shm_t *shm = NULL;
2321
2322    assert(caches);
2323
2324    for (i = 0; i < hcache->size; i ++) {
2325        xc_cache_t *cache = &caches[i];
2326        if (cache) {
2327            if (cache->lck) {
2328                xc_lock_destroy(cache->lck);
2329            }
2330            /* do NOT touch cached data */
2331            shm = cache->shm;
2332            cache->shm->handlers->memdestroy(cache->allocator);
2333        }
2334    }
2335    free(caches);
2336    return shm;
2337}
2338/* }}} */
2339static 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) /* {{{ */
2340{
2341    xc_cache_t *caches = NULL;
2342    xc_allocator_t *allocator;
2343    time_t now = time(NULL);
2344    size_t i;
2345    xc_memsize_t memsize;
2346
2347    memsize = shmsize / hcache->size;
2348
2349    /* Don't let it break out of mem after ALIGNed
2350     * This is important for
2351     * Simply loop until it fit our need
2352     */
2353    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
2354        if (memsize < ALIGN(1)) {
2355            CHECK(NULL, "cache too small");
2356        }
2357        memsize --;
2358    }
2359
2360    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t)), "caches OOM");
2361
2362    for (i = 0; i < hcache->size; i ++) {
2363        xc_cache_t *cache = &caches[i];
2364        CHECK(allocator = shm->handlers->meminit(shm, memsize), "Failed init shm");
2365        if (!(allocator->vtable = xc_allocator_find(allocator_name))) {
2366            zend_error(E_ERROR, "Allocator %s not found", allocator_name);
2367            goto err;
2368        }
2369        CHECK(allocator->vtable->init(shm, allocator, memsize), "Failed init allocator");
2370        CHECK(cache->cached           = allocator->vtable->calloc(allocator, 1, sizeof(xc_cached_t)), "cache OOM");
2371        CHECK(cache->cached->entries  = allocator->vtable->calloc(allocator, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
2372        if (hphp) {
2373            CHECK(cache->cached->phps = allocator->vtable->calloc(allocator, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
2374        }
2375        CHECK(cache->lck              = xc_lock_init(NULL), "can't create lock");
2376
2377        cache->hcache  = hcache;
2378        cache->hentry  = hentry;
2379        cache->hphp    = hphp;
2380        cache->shm     = shm;
2381        cache->allocator = allocator;
2382        cache->cacheid = i;
2383        cache->cached->last_gc_deletes = now;
2384        cache->cached->last_gc_expires = now;
2385    }
2386    return caches;
2387
2388err:
2389    if (caches) {
2390        xc_cache_destroy(caches, hcache);
2391    }
2392    return NULL;
2393}
2394/* }}} */
2395static void xc_destroy() /* {{{ */
2396{
2397    xc_shm_t *shm = NULL;
2398    if (old_compile_file && zend_compile_file == xc_compile_file) {
2399        zend_compile_file = old_compile_file;
2400        old_compile_file = NULL;
2401    }
2402
2403    if (xc_php_caches) {
2404        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
2405        xc_php_caches = NULL;
2406    }
2407
2408    if (xc_var_caches) {
2409        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
2410        xc_var_caches = NULL;
2411    }
2412
2413    if (shm) {
2414        xc_shm_destroy(shm);
2415    }
2416
2417    xc_initized = 0;
2418}
2419/* }}} */
2420static int xc_init() /* {{{ */
2421{
2422    xc_shm_t *shm = NULL;
2423    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
2424
2425    xc_php_caches = xc_var_caches = NULL;
2426
2427    if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
2428        zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
2429        goto err;
2430    }
2431
2432    if (xc_php_size || xc_var_size) {
2433        CHECK(shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
2434        if (!shm->handlers->can_readonly(shm)) {
2435            xc_readonly_protection = 0;
2436        }
2437
2438        if (xc_php_size) {
2439            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");
2440        }
2441
2442        if (xc_var_size) {
2443            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");
2444        }
2445    }
2446    return SUCCESS;
2447
2448err:
2449    if (xc_php_caches || xc_var_caches) {
2450        xc_destroy();
2451        /* shm destroied in xc_destroy() */
2452    }
2453    else if (shm) {
2454        xc_destroy();
2455        xc_shm_destroy(shm);
2456        shm = NULL;
2457    }
2458    return 0;
2459}
2460/* }}} */
2461static void xc_request_init(TSRMLS_D) /* {{{ */
2462{
2463    size_t i;
2464
2465    if (!XG(internal_table_copied)) {
2466        zend_function tmp_func;
2467        xc_cest_t tmp_cest;
2468
2469#ifdef HAVE_XCACHE_CONSTANT
2470        zend_hash_destroy(&XG(internal_constant_table));
2471#endif
2472        zend_hash_destroy(&XG(internal_function_table));
2473        zend_hash_destroy(&XG(internal_class_table));
2474
2475#ifdef HAVE_XCACHE_CONSTANT
2476        zend_hash_init_ex(&XG(internal_constant_table), 20,  NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2477#endif
2478        zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
2479        zend_hash_init_ex(&XG(internal_class_table),    10,  NULL, NULL, 1, 0);
2480
2481#ifdef HAVE_XCACHE_CONSTANT
2482        xc_copy_internal_zend_constants(&XG(internal_constant_table), EG(zend_constants));
2483#endif
2484        zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
2485        zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
2486
2487        XG(internal_table_copied) = 1;
2488    }
2489    if (xc_php_caches && !XG(php_holds)) {
2490        XG(php_holds_size) = xc_php_hcache.size;
2491        XG(php_holds) = calloc(XG(php_holds_size), sizeof(xc_stack_t));
2492        for (i = 0; i < xc_php_hcache.size; i ++) {
2493            xc_stack_init(&XG(php_holds[i]));
2494        }
2495    }
2496
2497    if (xc_var_caches && !XG(var_holds)) {
2498        XG(var_holds_size) = xc_var_hcache.size;
2499        XG(var_holds) = calloc(XG(var_holds_size), sizeof(xc_stack_t));
2500        for (i = 0; i < xc_var_hcache.size; i ++) {
2501            xc_stack_init(&XG(var_holds[i]));
2502        }
2503    }
2504
2505#ifdef ZEND_ENGINE_2
2506    zend_llist_init(&XG(gc_op_arrays), sizeof(xc_gc_op_array_t), xc_gc_op_array, 0);
2507#endif
2508
2509#if PHP_API_VERSION <= 20041225
2510    XG(request_time) = time(NULL);
2511#else
2512    XG(request_time) = sapi_get_request_time(TSRMLS_C);
2513#endif
2514}
2515/* }}} */
2516static void xc_request_shutdown(TSRMLS_D) /* {{{ */
2517{
2518    xc_entry_unholds(TSRMLS_C);
2519    xc_gc_expires_php(TSRMLS_C);
2520    xc_gc_expires_var(TSRMLS_C);
2521    xc_gc_deletes(TSRMLS_C);
2522#ifdef ZEND_ENGINE_2
2523    zend_llist_destroy(&XG(gc_op_arrays));
2524#endif
2525}
2526/* }}} */
2527
2528/* user functions */
2529static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
2530{
2531    zval **server = NULL;
2532    zval **user = NULL;
2533    zval **pass = NULL;
2534    char *admin_user = NULL;
2535    char *admin_pass = NULL;
2536    HashTable *ht;
2537
2538    /* auth disabled, nothing to do.. */
2539    if (!xc_admin_enable_auth) {
2540        return 1;
2541    }
2542
2543    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
2544        admin_user = NULL;
2545    }
2546    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
2547        admin_pass = NULL;
2548    }
2549
2550    if (admin_user == NULL || admin_pass == NULL) {
2551        php_error_docref(XCACHE_WIKI_URL "/InstallAdministration" TSRMLS_CC, E_ERROR,
2552                "xcache.admin.user and/or xcache.admin.pass settings is not configured."
2553                " Make sure you've modified the correct php ini file for your php used in webserver.");
2554        zend_bailout();
2555    }
2556    if (strlen(admin_pass) != 32) {
2557        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));
2558        zend_bailout();
2559    }
2560
2561#ifdef ZEND_ENGINE_2_1
2562    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
2563#endif
2564    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
2565        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
2566        zend_bailout();
2567    }
2568    ht = HASH_OF((*server));
2569
2570    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
2571        user = NULL;
2572    }
2573    else if (Z_TYPE_PP(user) != IS_STRING) {
2574        user = NULL;
2575    }
2576
2577    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
2578        pass = NULL;
2579    }
2580    else if (Z_TYPE_PP(pass) != IS_STRING) {
2581        pass = NULL;
2582    }
2583
2584    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
2585        PHP_MD5_CTX context;
2586        char md5str[33];
2587        unsigned char digest[16];
2588
2589        PHP_MD5Init(&context);
2590        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
2591        PHP_MD5Final(digest, &context);
2592
2593        md5str[0] = '\0';
2594        make_digest(md5str, digest);
2595        if (strcmp(admin_pass, md5str) == 0) {
2596            return 1;
2597        }
2598    }
2599
2600#define STR "HTTP/1.0 401 Unauthorized"
2601    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2602#undef STR
2603#define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
2604    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2605#undef STR
2606#define STR "Content-type: text/html; charset=UTF-8"
2607    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2608#undef STR
2609    ZEND_PUTS("<html>\n");
2610    ZEND_PUTS("<head><title>XCache Authentication Failed</title></head>\n");
2611    ZEND_PUTS("<body>\n");
2612    ZEND_PUTS("<h1>XCache Authentication Failed</h1>\n");
2613    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");
2614    ZEND_PUTS("<ul>\n");
2615    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");
2616    ZEND_PUTS("<li>Make sure the md5 password is generated correctly. You may use <a href=\"mkpassword.php\">mkpassword.php</a></li>\n");
2617    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");
2618    ZEND_PUTS("</ul>\n");
2619    ZEND_PUTS("Check <a href=\"" XCACHE_WIKI_URL "/InstallAdministration\">XCache wiki page</a> for more information.\n");
2620    ZEND_PUTS("</body>\n");
2621    ZEND_PUTS("</html>\n");
2622
2623    zend_bailout();
2624    return 0;
2625}
2626/* }}} */
2627static void xc_clear(long type, xc_cache_t *cache TSRMLS_DC) /* {{{ */
2628{
2629    xc_entry_t *e, *next;
2630    int entryslotid, c;
2631
2632    ENTER_LOCK(cache) {
2633        for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
2634            for (e = cache->cached->entries[entryslotid]; e; e = next) {
2635                next = e->next;
2636                xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
2637            }
2638            cache->cached->entries[entryslotid] = NULL;
2639        }
2640    } LEAVE_LOCK(cache);
2641} /* }}} */
2642/* {{{ xcache_admin_operate */
2643typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR, XC_OP_ENABLE } xcache_op_type;
2644static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
2645{
2646    long type;
2647    long size;
2648    xc_cache_t *caches, *cache;
2649    long id = 0;
2650    zend_bool enable = 1;
2651
2652    xcache_admin_auth_check(TSRMLS_C);
2653
2654    if (!xc_initized) {
2655        RETURN_NULL();
2656    }
2657
2658    switch (optype) {
2659        case XC_OP_COUNT:
2660            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2661                return;
2662            }
2663            break;
2664
2665        case XC_OP_CLEAR:
2666            id = -1;
2667            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &type, &id) == FAILURE) {
2668                return;
2669            }
2670            break;
2671
2672        case XC_OP_ENABLE:
2673            id = -1;
2674            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lb", &type, &id, &enable) == FAILURE) {
2675                return;
2676            }
2677            break;
2678
2679        default:
2680            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
2681                return;
2682            }
2683    }
2684
2685    switch (type) {
2686        case XC_TYPE_PHP:
2687            size = xc_php_hcache.size;
2688            caches = xc_php_caches;
2689            break;
2690
2691        case XC_TYPE_VAR:
2692            size = xc_var_hcache.size;
2693            caches = xc_var_caches;
2694            break;
2695
2696        default:
2697            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
2698            RETURN_FALSE;
2699    }
2700
2701    switch (optype) {
2702        case XC_OP_COUNT:
2703            RETURN_LONG(caches ? size : 0)
2704            break;
2705
2706        case XC_OP_INFO:
2707        case XC_OP_LIST:
2708            if (!caches || id < 0 || id >= size) {
2709                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2710                RETURN_FALSE;
2711            }
2712
2713            array_init(return_value);
2714
2715            cache = &caches[id];
2716            ENTER_LOCK(cache) {
2717                if (optype == XC_OP_INFO) {
2718                    xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
2719                }
2720                else {
2721                    xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
2722                }
2723            } LEAVE_LOCK(cache);
2724            break;
2725
2726        case XC_OP_CLEAR:
2727            if (!caches || id < -1 || id >= size) {
2728                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2729                RETURN_FALSE;
2730            }
2731
2732            if (id == -1) {
2733                for (id = 0; id < size; ++id) {
2734                    xc_clear(type, &caches[id] TSRMLS_CC);
2735                }
2736            }
2737            else {
2738                xc_clear(type, &caches[id] TSRMLS_CC);
2739            }
2740
2741            xc_gc_deletes(TSRMLS_C);
2742            break;
2743
2744        case XC_OP_ENABLE:
2745            if (!caches || id < -1 || id >= size) {
2746                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2747                RETURN_FALSE;
2748            }
2749
2750            if (id == -1) {
2751                for (id = 0; id < size; ++id) {
2752                    caches[id].cached->disabled = !enable ? XG(request_time) : 0;
2753                }
2754            }
2755            else {
2756                caches[id].cached->disabled = !enable ? XG(request_time) : 0;
2757            }
2758
2759            break;
2760
2761        default:
2762            assert(0);
2763    }
2764}
2765/* }}} */
2766/* {{{ proto int xcache_count(int type)
2767   Return count of cache on specified cache type */
2768PHP_FUNCTION(xcache_count)
2769{
2770    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2771}
2772/* }}} */
2773/* {{{ proto array xcache_info(int type, int id)
2774   Get cache info by id on specified cache type */
2775PHP_FUNCTION(xcache_info)
2776{
2777    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2778}
2779/* }}} */
2780/* {{{ proto array xcache_list(int type, int id)
2781   Get cache entries list by id on specified cache type */
2782PHP_FUNCTION(xcache_list)
2783{
2784    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2785}
2786/* }}} */
2787/* {{{ proto array xcache_clear_cache(int type, [ int id = -1 ])
2788   Clear cache by id on specified cache type */
2789PHP_FUNCTION(xcache_clear_cache)
2790{
2791    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2792}
2793/* }}} */
2794/* {{{ proto array xcache_enable_cache(int type, [ int id = -1, [ bool enable = true ] ])
2795   Enable or disable cache by id on specified cache type */
2796PHP_FUNCTION(xcache_enable_cache)
2797{
2798    xcache_admin_operate(XC_OP_ENABLE, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2799}
2800/* }}} */
2801
2802#define VAR_CACHE_NOT_INITIALIZED() do { \
2803        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache var cache was not initialized properly. Check php log for actual reason"); \
2804} while (0)
2805
2806static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
2807{
2808    xc_hash_value_t hv;
2809
2810    switch (name->type) {
2811#ifdef IS_UNICODE
2812        case IS_UNICODE:
2813        case IS_STRING:
2814#endif
2815        default:
2816#ifdef IS_UNICODE
2817            convert_to_unicode(name);
2818#else
2819            convert_to_string(name);
2820#endif
2821    }
2822
2823#ifdef IS_UNICODE
2824    entry_var->name_type = name->type;
2825#endif
2826    entry_var->entry.name = name->value;
2827
2828    hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
2829
2830    entry_hash->cacheid = (hv & xc_var_hcache.mask);
2831    hv >>= xc_var_hcache.bits;
2832    entry_hash->entryslotid = (hv & xc_var_hentry.mask);
2833    return SUCCESS;
2834}
2835/* }}} */
2836/* {{{ proto mixed xcache_get(string name)
2837   Get cached data by specified name */
2838PHP_FUNCTION(xcache_get)
2839{
2840    xc_entry_hash_t entry_hash;
2841    xc_cache_t *cache;
2842    xc_entry_var_t entry_var, *stored_entry_var;
2843    zval *name;
2844
2845    if (!xc_var_caches) {
2846        VAR_CACHE_NOT_INITIALIZED();
2847        RETURN_NULL();
2848    }
2849
2850    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2851        return;
2852    }
2853    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2854    cache = &xc_var_caches[entry_hash.cacheid];
2855
2856    if (cache->cached->disabled) {
2857        RETURN_NULL();
2858    }
2859
2860    ENTER_LOCK(cache) {
2861        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);
2862        if (stored_entry_var) {
2863            /* return */
2864            xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
2865            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2866        }
2867        else {
2868            RETVAL_NULL();
2869        }
2870    } LEAVE_LOCK(cache);
2871}
2872/* }}} */
2873/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
2874   Store data to cache by specified name */
2875PHP_FUNCTION(xcache_set)
2876{
2877    xc_entry_hash_t entry_hash;
2878    xc_cache_t *cache;
2879    xc_entry_var_t entry_var, *stored_entry_var;
2880    zval *name;
2881    zval *value;
2882
2883    if (!xc_var_caches) {
2884        VAR_CACHE_NOT_INITIALIZED();
2885        RETURN_NULL();
2886    }
2887
2888    entry_var.entry.ttl = XG(var_ttl);
2889    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
2890        return;
2891    }
2892
2893    if (Z_TYPE_P(value) == IS_OBJECT) {
2894        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Objects cannot be stored in the variable cache. Use serialize before xcache_set");
2895        RETURN_NULL();
2896    }
2897
2898    /* max ttl */
2899    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
2900        entry_var.entry.ttl = xc_var_maxttl;
2901    }
2902
2903    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2904    cache = &xc_var_caches[entry_hash.cacheid];
2905
2906    if (cache->cached->disabled) {
2907        RETURN_NULL();
2908    }
2909
2910    ENTER_LOCK(cache) {
2911        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);
2912        if (stored_entry_var) {
2913            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2914        }
2915        entry_var.value = value;
2916        RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
2917    } LEAVE_LOCK(cache);
2918}
2919/* }}} */
2920/* {{{ proto bool  xcache_isset(string name)
2921   Check if an entry exists in cache by specified name */
2922PHP_FUNCTION(xcache_isset)
2923{
2924    xc_entry_hash_t entry_hash;
2925    xc_cache_t *cache;
2926    xc_entry_var_t entry_var, *stored_entry_var;
2927    zval *name;
2928
2929    if (!xc_var_caches) {
2930        VAR_CACHE_NOT_INITIALIZED();
2931        RETURN_FALSE;
2932    }
2933
2934    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2935        return;
2936    }
2937    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2938    cache = &xc_var_caches[entry_hash.cacheid];
2939
2940    if (cache->cached->disabled) {
2941        RETURN_FALSE;
2942    }
2943
2944    ENTER_LOCK(cache) {
2945        stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
2946        if (stored_entry_var) {
2947            xc_cached_hit_unlocked(cache->cached TSRMLS_CC);
2948            RETVAL_TRUE;
2949            /* return */
2950        }
2951        else {
2952            RETVAL_FALSE;
2953        }
2954
2955    } LEAVE_LOCK(cache);
2956}
2957/* }}} */
2958/* {{{ proto bool  xcache_unset(string name)
2959   Unset existing data in cache by specified name */
2960PHP_FUNCTION(xcache_unset)
2961{
2962    xc_entry_hash_t entry_hash;
2963    xc_cache_t *cache;
2964    xc_entry_var_t entry_var, *stored_entry_var;
2965    zval *name;
2966
2967    if (!xc_var_caches) {
2968        VAR_CACHE_NOT_INITIALIZED();
2969        RETURN_FALSE;
2970    }
2971
2972    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2973        return;
2974    }
2975    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2976    cache = &xc_var_caches[entry_hash.cacheid];
2977
2978    if (cache->cached->disabled) {
2979        RETURN_FALSE;
2980    }
2981
2982    ENTER_LOCK(cache) {
2983        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);
2984        if (stored_entry_var) {
2985            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2986            RETVAL_TRUE;
2987        }
2988        else {
2989            RETVAL_FALSE;
2990        }
2991    } LEAVE_LOCK(cache);
2992}
2993/* }}} */
2994/* {{{ proto bool  xcache_unset_by_prefix(string prefix)
2995   Unset existing data in cache by specified prefix */
2996PHP_FUNCTION(xcache_unset_by_prefix)
2997{
2998    zval *prefix;
2999    int i, iend;
3000
3001    if (!xc_var_caches) {
3002        VAR_CACHE_NOT_INITIALIZED();
3003        RETURN_FALSE;
3004    }
3005
3006    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
3007        return;
3008    }
3009
3010    for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
3011        xc_cache_t *cache = &xc_var_caches[i];
3012        if (cache->cached->disabled) {
3013            continue;
3014        }
3015
3016        ENTER_LOCK(cache) {
3017            int entryslotid, jend;
3018            for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
3019                xc_entry_t *entry, *next;
3020                for (entry = cache->cached->entries[entryslotid]; entry; entry = next) {
3021                    next = entry->next;
3022                    if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
3023                        xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
3024                    }
3025                }
3026            }
3027        } LEAVE_LOCK(cache);
3028    }
3029}
3030/* }}} */
3031static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
3032{
3033    xc_entry_hash_t entry_hash;
3034    xc_cache_t *cache;
3035    xc_entry_var_t entry_var, *stored_entry_var;
3036    zval *name;
3037    long count = 1;
3038    long value = 0;
3039    zval oldzval;
3040
3041    if (!xc_var_caches) {
3042        VAR_CACHE_NOT_INITIALIZED();
3043        RETURN_NULL();
3044    }
3045
3046    entry_var.entry.ttl = XG(var_ttl);
3047    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
3048        return;
3049    }
3050
3051    /* max ttl */
3052    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
3053        entry_var.entry.ttl = xc_var_maxttl;
3054    }
3055
3056    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
3057    cache = &xc_var_caches[entry_hash.cacheid];
3058
3059    if (cache->cached->disabled) {
3060        RETURN_NULL();
3061    }
3062
3063    ENTER_LOCK(cache) {
3064        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);
3065        if (stored_entry_var) {
3066            TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
3067            /* do it in place */
3068            if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
3069                zval *zv;
3070                stored_entry_var->entry.ctime = XG(request_time);
3071                stored_entry_var->entry.ttl   = entry_var.entry.ttl;
3072                TRACE("%s", "incdec: islong");
3073                value = Z_LVAL_P(stored_entry_var->value);
3074                value += (inc == 1 ? count : - count);
3075                RETVAL_LONG(value);
3076
3077                zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
3078                Z_LVAL_P(zv) = value;
3079                ++cache->cached->updates;
3080                break; /* leave lock */
3081            }
3082
3083            TRACE("%s", "incdec: notlong");
3084            xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
3085            convert_to_long(&oldzval);
3086            value = Z_LVAL(oldzval);
3087            zval_dtor(&oldzval);
3088        }
3089        else {
3090            TRACE("incdec: %s not found", entry_var.entry.name.str.val);
3091        }
3092
3093        value += (inc == 1 ? count : - count);
3094        RETVAL_LONG(value);
3095        entry_var.value = return_value;
3096
3097        if (stored_entry_var) {
3098            entry_var.entry.atime = stored_entry_var->entry.atime;
3099            entry_var.entry.ctime = stored_entry_var->entry.ctime;
3100            entry_var.entry.hits  = stored_entry_var->entry.hits;
3101            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
3102        }
3103        xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
3104    } LEAVE_LOCK(cache);
3105}
3106/* }}} */
3107/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
3108   Increase an int counter in cache by specified name, create it if not exists */
3109PHP_FUNCTION(xcache_inc)
3110{
3111    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3112}
3113/* }}} */
3114/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
3115   Decrease an int counter in cache by specified name, create it if not exists */
3116PHP_FUNCTION(xcache_dec)
3117{
3118    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3119}
3120/* }}} */
3121static zend_function_entry xcache_cacher_functions[] = /* {{{ */
3122{
3123    PHP_FE(xcache_count,             NULL)
3124    PHP_FE(xcache_info,              NULL)
3125    PHP_FE(xcache_list,              NULL)
3126    PHP_FE(xcache_clear_cache,       NULL)
3127    PHP_FE(xcache_enable_cache,      NULL)
3128    PHP_FE(xcache_get,               NULL)
3129    PHP_FE(xcache_set,               NULL)
3130    PHP_FE(xcache_isset,             NULL)
3131    PHP_FE(xcache_unset,             NULL)
3132    PHP_FE(xcache_unset_by_prefix,   NULL)
3133    PHP_FE(xcache_inc,               NULL)
3134    PHP_FE(xcache_dec,               NULL)
3135    PHP_FE_END
3136};
3137/* }}} */
3138
3139static int xc_cacher_zend_startup(zend_extension *extension) /* {{{ */
3140{
3141    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
3142        if (xc_init() != SUCCESS) {
3143            zend_error(E_ERROR, "XCache: Cannot init");
3144            return FAILURE;
3145        }
3146        xc_initized = 1;
3147        xc_init_time = time(NULL);
3148#ifdef PHP_WIN32
3149        xc_init_instance_id = GetCurrentProcessId();
3150#else
3151        xc_init_instance_id = getpid();
3152#endif
3153#ifdef ZTS
3154        xc_init_instance_subid = tsrm_thread_id();
3155#endif
3156    }
3157
3158    if (xc_php_size) {
3159        old_compile_file = zend_compile_file;
3160        zend_compile_file = xc_compile_file;
3161    }
3162
3163    return SUCCESS;
3164}
3165/* }}} */
3166static void xc_cacher_zend_shutdown(zend_extension *extension) /* {{{ */
3167{
3168    if (xc_initized) {
3169        xc_destroy();
3170    }
3171}
3172/* }}} */
3173/* {{{ zend extension definition structure */
3174static zend_extension xc_cacher_zend_extension_entry = {
3175    XCACHE_NAME " Cacher",
3176    XCACHE_VERSION,
3177    XCACHE_AUTHOR,
3178    XCACHE_URL,
3179    XCACHE_COPYRIGHT,
3180    xc_cacher_zend_startup,
3181    xc_cacher_zend_shutdown,
3182    NULL,           /* activate_func_t */
3183    NULL,           /* deactivate_func_t */
3184    NULL,           /* message_handler_func_t */
3185    NULL,           /* op_array_handler_func_t */
3186    NULL,           /* statement_handler_func_t */
3187    NULL,           /* fcall_begin_handler_func_t */
3188    NULL,           /* fcall_end_handler_func_t */
3189    NULL,           /* op_array_ctor_func_t */
3190    NULL,           /* op_array_dtor_func_t */
3191    STANDARD_ZEND_EXTENSION_PROPERTIES
3192};
3193/* }}} */
3194
3195/* {{{ ini */
3196#ifdef ZEND_WIN32
3197#   define DEFAULT_PATH "xcache"
3198#else
3199#   define DEFAULT_PATH "/dev/zero"
3200#endif
3201PHP_INI_BEGIN()
3202    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_shm_scheme)
3203    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_mmap_path)
3204    PHP_INI_ENTRY1_EX  ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_readonly_protection, zend_ini_boolean_displayer_cb)
3205    /* opcode cache */
3206    PHP_INI_ENTRY1_EX  ("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_admin_enable_auth,   zend_ini_boolean_displayer_cb)
3207    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3208    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3209    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3210    PHP_INI_ENTRY1     ("xcache.allocator",        "bestfit", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_php_allocator)
3211    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_ttl)
3212    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_gc_interval)
3213    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,    cacher, zend_xcache_globals, xcache_globals)
3214    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,    stat,   zend_xcache_globals, xcache_globals)
3215    /* var cache */
3216    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3217    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3218    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3219    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_maxttl)
3220    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_gc_interval)
3221    PHP_INI_ENTRY1     ("xcache.var_allocator",    "bestfit", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_var_allocator)
3222    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong, var_ttl,   zend_xcache_globals, xcache_globals)
3223PHP_INI_END()
3224/* }}} */
3225static PHP_MINFO_FUNCTION(xcache_cacher) /* {{{ */
3226{
3227    char buf[100];
3228    char *ptr;
3229    int left, len;
3230    xc_shm_scheme_t *scheme;
3231
3232    php_info_print_table_start();
3233    php_info_print_table_row(2, "XCache Cacher Module", "enabled");
3234    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "disabled");
3235#ifdef ZEND_ENGINE_2_1
3236    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, XG(request_time), 1 TSRMLS_CC);
3237    php_info_print_table_row(2, "Page Request Time", ptr);
3238    efree(ptr);
3239
3240    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
3241    php_info_print_table_row(2, "Cache Init Time", ptr);
3242    efree(ptr);
3243#else
3244    snprintf(buf, sizeof(buf), "%lu", (long unsigned) XG(request_time));
3245    php_info_print_table_row(2, "Page Request Time", buf);
3246
3247    snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
3248    php_info_print_table_row(2, "Cache Init Time", buf);
3249#endif
3250
3251#ifdef ZTS
3252    snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
3253#else
3254    snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
3255#endif
3256    php_info_print_table_row(2, "Cache Instance Id", buf);
3257
3258    if (xc_php_size) {
3259        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
3260        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);
3261        php_info_print_table_row(2, "Opcode Cache", buf);
3262        efree(ptr);
3263    }
3264    else {
3265        php_info_print_table_row(2, "Opcode Cache", "disabled");
3266    }
3267    if (xc_var_size) {
3268        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
3269        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);
3270        php_info_print_table_row(2, "Variable Cache", buf);
3271        efree(ptr);
3272    }
3273    else {
3274        php_info_print_table_row(2, "Variable Cache", "disabled");
3275    }
3276
3277    left = sizeof(buf);
3278    ptr = buf;
3279    buf[0] = '\0';
3280    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
3281        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
3282        left -= len;
3283        ptr += len;
3284    }
3285    php_info_print_table_row(2, "Shared Memory Schemes", buf);
3286
3287    php_info_print_table_end();
3288
3289    DISPLAY_INI_ENTRIES();
3290}
3291/* }}} */
3292static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
3293{
3294    size_t bits, size;
3295    char *value;
3296
3297    if (cfg_get_string(name, &value) != SUCCESS) {
3298        value = default_value;
3299    }
3300
3301    p->size = zend_atoi(value, strlen(value));
3302    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
3303        /* empty body */
3304    }
3305    p->size = size;
3306    p->bits = bits;
3307    p->mask = size - 1;
3308
3309    return SUCCESS;
3310}
3311/* }}} */
3312static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
3313{
3314    char *value;
3315
3316    if (cfg_get_string(name, &value) != SUCCESS) {
3317        value = default_value;
3318    }
3319
3320    *p = zend_atol(value, strlen(value));
3321    return SUCCESS;
3322}
3323/* }}} */
3324static PHP_MINIT_FUNCTION(xcache_cacher) /* {{{ */
3325{
3326    zend_extension *ext;
3327    zend_llist_position lpos;
3328
3329    ext = zend_get_extension("Zend Optimizer");
3330    if (ext) {
3331        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
3332        ext->op_array_handler = NULL;
3333    }
3334    /* cache if there's an op_array_ctor */
3335    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
3336            ext;
3337            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
3338        if (ext->op_array_ctor) {
3339            xc_have_op_array_ctor = 1;
3340            break;
3341        }
3342    }
3343
3344    xc_config_long(&xc_php_size,       "xcache.size",        "0");
3345    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
3346    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
3347
3348    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
3349    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
3350    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
3351
3352    if (strcmp(sapi_module.name, "cli") == 0) {
3353        if (!xc_test) {
3354            /* disable cache for cli except for testing */
3355            xc_php_size = xc_var_size = 0;
3356        }
3357    }
3358
3359    if (xc_php_size <= 0) {
3360        xc_php_size = xc_php_hcache.size = 0;
3361    }
3362    if (xc_var_size <= 0) {
3363        xc_var_size = xc_var_hcache.size = 0;
3364    }
3365
3366    xc_init_constant(module_number TSRMLS_CC);
3367
3368    REGISTER_INI_ENTRIES();
3369
3370    xc_sandbox_module_init(module_number TSRMLS_CC);
3371    return xcache_zend_extension_add(&xc_cacher_zend_extension_entry, 0);
3372}
3373/* }}} */
3374static PHP_MSHUTDOWN_FUNCTION(xcache_cacher) /* {{{ */
3375{
3376    xc_sandbox_module_shutdown();
3377
3378    xcache_zend_extension_remove(&xc_cacher_zend_extension_entry);
3379    UNREGISTER_INI_ENTRIES();
3380
3381    if (xc_mmap_path) {
3382        pefree(xc_mmap_path, 1);
3383        xc_mmap_path = NULL;
3384    }
3385    if (xc_shm_scheme) {
3386        pefree(xc_shm_scheme, 1);
3387        xc_shm_scheme = NULL;
3388    }
3389    if (xc_php_allocator) {
3390        pefree(xc_php_allocator, 1);
3391        xc_php_allocator = NULL;
3392    }
3393    if (xc_var_allocator) {
3394        pefree(xc_var_allocator, 1);
3395        xc_var_allocator = NULL;
3396    }
3397
3398    return SUCCESS;
3399}
3400/* }}} */
3401static PHP_RINIT_FUNCTION(xcache_cacher) /* {{{ */
3402{
3403    xc_request_init(TSRMLS_C);
3404    return SUCCESS;
3405}
3406/* }}} */
3407/* {{{ static PHP_RSHUTDOWN_FUNCTION(xcache_cacher) */
3408#ifndef ZEND_ENGINE_2
3409static PHP_RSHUTDOWN_FUNCTION(xcache_cacher)
3410#else
3411static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache_cacher)
3412#endif
3413{
3414#ifdef ZEND_ENGINE_2
3415    TSRMLS_FETCH();
3416#endif
3417
3418    xc_request_shutdown(TSRMLS_C);
3419    return SUCCESS;
3420}
3421/* }}} */
3422static zend_module_entry xcache_cacher_module_entry = { /* {{{ */
3423    STANDARD_MODULE_HEADER,
3424    XCACHE_NAME " Cacher",
3425    xcache_cacher_functions,
3426    PHP_MINIT(xcache_cacher),
3427    PHP_MSHUTDOWN(xcache_cacher),
3428    PHP_RINIT(xcache_cacher),
3429#ifndef ZEND_ENGINE_2
3430    PHP_RSHUTDOWN(xcache_cacher),
3431#else
3432    NULL,
3433#endif
3434    PHP_MINFO(xcache_cacher),
3435    XCACHE_VERSION,
3436#ifdef PHP_GINIT
3437    NO_MODULE_GLOBALS,
3438#endif
3439#ifdef ZEND_ENGINE_2
3440    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache_cacher),
3441#else
3442    NULL,
3443    NULL,
3444#endif
3445    STANDARD_MODULE_PROPERTIES_EX
3446};
3447/* }}} */
3448int xc_cacher_startup_module() /* {{{ */
3449{
3450    return zend_startup_module(&xcache_cacher_module_entry);
3451}
3452/* }}} */
3453void xc_cacher_disable() /* {{{ */
3454{
3455    time_t now = time(NULL);
3456    size_t i;
3457
3458    if (xc_php_caches) {
3459        for (i = 0; i < xc_php_hcache.size; i ++) {
3460            if (xc_php_caches[i].cached) {
3461                xc_php_caches[i].cached->disabled = now;
3462            }
3463        }
3464    }
3465
3466    if (xc_var_caches) {
3467        for (i = 0; i < xc_var_hcache.size; i ++) {
3468            if (xc_var_caches[i].cached) {
3469                xc_var_caches[i].cached->disabled = now;
3470            }
3471        }
3472    }
3473}
3474/* }}} */
Note: See TracBrowser for help on using the repository browser.