source: trunk/xcache.c @ 936

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

change misses to updates. misses is kept for compatibility reason but deprecated

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