source: trunk/mod_cacher/xc_cacher.c @ 1178

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

improve compatibility with "the ionCube PHP Loader", Zend Optimizer. dont do early binding for cached opcode

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