source: trunk/xcache.c @ 975

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

avoid bogus warning for fcgi

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