source: trunk/xcache.c @ 998

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

refactor: fix build, easier devel

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