source: trunk/xcache.c @ 954

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

restore open_files handling, it is required. disable included_files which is real EG since PHP 5_2

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