source: trunk/mod_cacher/xc_cacher.c @ 1453

Last change on this file since 1453 was 1453, checked in by moo, 12 months ago

update func prototype for relocate

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