source: trunk/mod_cacher/xc_cacher.c @ 1446

Last change on this file since 1446 was 1446, checked in by moo, 14 months ago

update relocate

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