source: trunk/xcache.c @ 978

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

fix build. additional_functions requires PHP 5.3

  • Property svn:eol-style set to native
File size: 119.2 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/* }}} */
2685static void xc_clear(long type, xc_cache_t *cache TSRMLS_DC) /* {{{ */
2686{
2687    xc_entry_t *e, *next;
2688    int entryslotid, c;
2689
2690    ENTER_LOCK(cache) {
2691        for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
2692            for (e = cache->entries[entryslotid]; e; e = next) {
2693                next = e->next;
2694                xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
2695            }
2696            cache->entries[entryslotid] = NULL;
2697        }
2698    } LEAVE_LOCK(cache);
2699} /* }}} */
2700/* {{{ xcache_admin_operate */
2701typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
2702static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
2703{
2704    long type;
2705    int size;
2706    xc_cache_t **caches, *cache;
2707    long id = 0;
2708
2709    xcache_admin_auth_check(TSRMLS_C);
2710
2711    if (!xc_initized) {
2712        RETURN_NULL();
2713    }
2714
2715    switch (optype) {
2716        case XC_OP_COUNT:
2717            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2718                return;
2719            }
2720            break;
2721        case XC_OP_CLEAR:
2722            id = -1;
2723            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &type, &id) == FAILURE) {
2724                return;
2725            }
2726            break;
2727        default:
2728            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
2729                return;
2730            }
2731    }
2732
2733    switch (type) {
2734        case XC_TYPE_PHP:
2735            size = xc_php_hcache.size;
2736            caches = xc_php_caches;
2737            break;
2738
2739        case XC_TYPE_VAR:
2740            size = xc_var_hcache.size;
2741            caches = xc_var_caches;
2742            break;
2743
2744        default:
2745            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
2746            RETURN_FALSE;
2747    }
2748
2749    switch (optype) {
2750        case XC_OP_COUNT:
2751            RETURN_LONG(caches ? size : 0)
2752            break;
2753
2754        case XC_OP_INFO:
2755        case XC_OP_LIST:
2756            if (!caches || id < 0 || id >= size) {
2757                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2758                RETURN_FALSE;
2759            }
2760
2761            array_init(return_value);
2762
2763            cache = caches[id];
2764            ENTER_LOCK(cache) {
2765                if (optype == XC_OP_INFO) {
2766                    xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
2767                }
2768                else {
2769                    xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
2770                }
2771            } LEAVE_LOCK(cache);
2772            break;
2773
2774        case XC_OP_CLEAR:
2775            if (!caches || id < -1 || id >= size) {
2776                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2777                RETURN_FALSE;
2778            }
2779
2780            if (id == -1) {
2781                for (id = 0; id < size; ++id) {
2782                    xc_clear(type, caches[id] TSRMLS_CC);
2783                }
2784            }
2785            else {
2786                xc_clear(type, caches[id] TSRMLS_CC);
2787            }
2788
2789            xc_gc_deletes(TSRMLS_C);
2790            break;
2791
2792        default:
2793            assert(0);
2794    }
2795}
2796/* }}} */
2797/* {{{ proto int xcache_count(int type)
2798   Return count of cache on specified cache type */
2799PHP_FUNCTION(xcache_count)
2800{
2801    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2802}
2803/* }}} */
2804/* {{{ proto array xcache_info(int type, int id)
2805   Get cache info by id on specified cache type */
2806PHP_FUNCTION(xcache_info)
2807{
2808    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2809}
2810/* }}} */
2811/* {{{ proto array xcache_list(int type, int id)
2812   Get cache entries list by id on specified cache type */
2813PHP_FUNCTION(xcache_list)
2814{
2815    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2816}
2817/* }}} */
2818/* {{{ proto array xcache_clear_cache(int type, [ int id = -1 ])
2819   Clear cache by id on specified cache type */
2820PHP_FUNCTION(xcache_clear_cache)
2821{
2822    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2823}
2824/* }}} */
2825
2826#define VAR_DISABLED_WARNING() do { \
2827        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache var cache was not initialized properly. Check php log for actual reason"); \
2828} while (0)
2829
2830static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
2831{
2832    xc_hash_value_t hv;
2833
2834    switch (name->type) {
2835#ifdef IS_UNICODE
2836        case IS_UNICODE:
2837        case IS_STRING:
2838#endif
2839        default:
2840#ifdef IS_UNICODE
2841            convert_to_unicode(name);
2842#else
2843            convert_to_string(name);
2844#endif
2845    }
2846
2847#ifdef IS_UNICODE
2848    entry_var->name_type = name->type;
2849#endif
2850    entry_var->entry.name = name->value;
2851
2852    hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
2853
2854    entry_hash->cacheid = (hv & xc_var_hcache.mask);
2855    hv >>= xc_var_hcache.bits;
2856    entry_hash->entryslotid = (hv & xc_var_hentry.mask);
2857    return SUCCESS;
2858}
2859/* }}} */
2860/* {{{ proto mixed xcache_get(string name)
2861   Get cached data by specified name */
2862PHP_FUNCTION(xcache_get)
2863{
2864    xc_entry_hash_t entry_hash;
2865    xc_cache_t *cache;
2866    xc_entry_var_t entry_var, *stored_entry_var;
2867    zval *name;
2868
2869    if (!xc_var_caches) {
2870        VAR_DISABLED_WARNING();
2871        RETURN_NULL();
2872    }
2873
2874    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2875        return;
2876    }
2877    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2878    cache = xc_var_caches[entry_hash.cacheid];
2879
2880    ENTER_LOCK(cache) {
2881        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);
2882        if (stored_entry_var) {
2883            /* return */
2884            xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
2885            xc_cache_hit_unlocked(cache TSRMLS_CC);
2886        }
2887        else {
2888            RETVAL_NULL();
2889        }
2890    } LEAVE_LOCK(cache);
2891}
2892/* }}} */
2893/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
2894   Store data to cache by specified name */
2895PHP_FUNCTION(xcache_set)
2896{
2897    xc_entry_hash_t entry_hash;
2898    xc_cache_t *cache;
2899    xc_entry_var_t entry_var, *stored_entry_var;
2900    zval *name;
2901    zval *value;
2902
2903    if (!xc_var_caches) {
2904        VAR_DISABLED_WARNING();
2905        RETURN_NULL();
2906    }
2907
2908    entry_var.entry.ttl = XG(var_ttl);
2909    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
2910        return;
2911    }
2912
2913    if (Z_TYPE_P(value) == IS_OBJECT) {
2914        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Objects cannot be stored in the variable cache. Use serialize before xcache_set");
2915        RETURN_NULL();
2916    }
2917
2918    /* max ttl */
2919    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
2920        entry_var.entry.ttl = xc_var_maxttl;
2921    }
2922
2923    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2924    cache = xc_var_caches[entry_hash.cacheid];
2925
2926    ENTER_LOCK(cache) {
2927        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);
2928        if (stored_entry_var) {
2929            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2930        }
2931        entry_var.value = value;
2932        RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
2933    } LEAVE_LOCK(cache);
2934}
2935/* }}} */
2936/* {{{ proto bool  xcache_isset(string name)
2937   Check if an entry exists in cache by specified name */
2938PHP_FUNCTION(xcache_isset)
2939{
2940    xc_entry_hash_t entry_hash;
2941    xc_cache_t *cache;
2942    xc_entry_var_t entry_var, *stored_entry_var;
2943    zval *name;
2944
2945    if (!xc_var_caches) {
2946        VAR_DISABLED_WARNING();
2947        RETURN_FALSE;
2948    }
2949
2950    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2951        return;
2952    }
2953    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2954    cache = xc_var_caches[entry_hash.cacheid];
2955
2956    ENTER_LOCK(cache) {
2957        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);
2958        if (stored_entry_var) {
2959            xc_cache_hit_unlocked(cache TSRMLS_CC);
2960            RETVAL_TRUE;
2961            /* return */
2962        }
2963        else {
2964            RETVAL_FALSE;
2965        }
2966
2967    } LEAVE_LOCK(cache);
2968}
2969/* }}} */
2970/* {{{ proto bool  xcache_unset(string name)
2971   Unset existing data in cache by specified name */
2972PHP_FUNCTION(xcache_unset)
2973{
2974    xc_entry_hash_t entry_hash;
2975    xc_cache_t *cache;
2976    xc_entry_var_t entry_var, *stored_entry_var;
2977    zval *name;
2978
2979    if (!xc_var_caches) {
2980        VAR_DISABLED_WARNING();
2981        RETURN_FALSE;
2982    }
2983
2984    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2985        return;
2986    }
2987    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2988    cache = xc_var_caches[entry_hash.cacheid];
2989
2990    ENTER_LOCK(cache) {
2991        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);
2992        if (stored_entry_var) {
2993            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2994            RETVAL_TRUE;
2995        }
2996        else {
2997            RETVAL_FALSE;
2998        }
2999    } LEAVE_LOCK(cache);
3000}
3001/* }}} */
3002/* {{{ proto bool  xcache_unset_by_prefix(string prefix)
3003   Unset existing data in cache by specified prefix */
3004PHP_FUNCTION(xcache_unset_by_prefix)
3005{
3006    zval *prefix;
3007    int i, iend;
3008
3009    if (!xc_var_caches) {
3010        VAR_DISABLED_WARNING();
3011        RETURN_FALSE;
3012    }
3013
3014    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
3015        return;
3016    }
3017
3018    for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
3019        xc_cache_t *cache = xc_var_caches[i];
3020        ENTER_LOCK(cache) {
3021            int entryslotid, jend;
3022            for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
3023                xc_entry_t *entry, *next;
3024                for (entry = cache->entries[entryslotid]; entry; entry = next) {
3025                    next = entry->next;
3026                    if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
3027                        xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
3028                    }
3029                }
3030            }
3031        } LEAVE_LOCK(cache);
3032    }
3033}
3034/* }}} */
3035static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
3036{
3037    xc_entry_hash_t entry_hash;
3038    xc_cache_t *cache;
3039    xc_entry_var_t entry_var, *stored_entry_var;
3040    zval *name;
3041    long count = 1;
3042    long value = 0;
3043    zval oldzval;
3044
3045    if (!xc_var_caches) {
3046        VAR_DISABLED_WARNING();
3047        RETURN_NULL();
3048    }
3049
3050    entry_var.entry.ttl = XG(var_ttl);
3051    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
3052        return;
3053    }
3054
3055    /* max ttl */
3056    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
3057        entry_var.entry.ttl = xc_var_maxttl;
3058    }
3059
3060    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
3061    cache = xc_var_caches[entry_hash.cacheid];
3062
3063    ENTER_LOCK(cache) {
3064        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);
3065        if (stored_entry_var) {
3066            TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
3067            /* do it in place */
3068            if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
3069                zval *zv;
3070                stored_entry_var->entry.ctime = XG(request_time);
3071                stored_entry_var->entry.ttl   = entry_var.entry.ttl;
3072                TRACE("%s", "incdec: islong");
3073                value = Z_LVAL_P(stored_entry_var->value);
3074                value += (inc == 1 ? count : - count);
3075                RETVAL_LONG(value);
3076
3077                zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
3078                Z_LVAL_P(zv) = value;
3079                ++cache->updates;
3080                break; /* leave lock */
3081            }
3082
3083            TRACE("%s", "incdec: notlong");
3084            xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
3085            convert_to_long(&oldzval);
3086            value = Z_LVAL(oldzval);
3087            zval_dtor(&oldzval);
3088        }
3089        else {
3090            TRACE("incdec: %s not found", entry_var.entry.name.str.val);
3091        }
3092
3093        value += (inc == 1 ? count : - count);
3094        RETVAL_LONG(value);
3095        entry_var.value = return_value;
3096
3097        if (stored_entry_var) {
3098            entry_var.entry.atime = stored_entry_var->entry.atime;
3099            entry_var.entry.ctime = stored_entry_var->entry.ctime;
3100            entry_var.entry.hits  = stored_entry_var->entry.hits;
3101            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
3102        }
3103        xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
3104    } LEAVE_LOCK(cache);
3105}
3106/* }}} */
3107/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
3108   Increase an int counter in cache by specified name, create it if not exists */
3109PHP_FUNCTION(xcache_inc)
3110{
3111    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3112}
3113/* }}} */
3114/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
3115   Decrease an int counter in cache by specified name, create it if not exists */
3116PHP_FUNCTION(xcache_dec)
3117{
3118    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3119}
3120/* }}} */
3121/* {{{ proto int xcache_get_refcount(mixed variable)
3122   XCache internal uses only: Get reference count of variable */
3123PHP_FUNCTION(xcache_get_refcount)
3124{
3125    zval *variable;
3126    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3127        RETURN_NULL();
3128    }
3129
3130    RETURN_LONG(Z_REFCOUNT(*variable));
3131}
3132/* }}} */
3133/* {{{ proto bool xcache_get_isref(mixed variable)
3134   XCache internal uses only: Check if variable data is marked referenced */
3135ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
3136    ZEND_ARG_INFO(1, variable)
3137ZEND_END_ARG_INFO()
3138
3139PHP_FUNCTION(xcache_get_isref)
3140{
3141    zval *variable;
3142    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3143        RETURN_NULL();
3144    }
3145
3146    RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
3147}
3148/* }}} */
3149#ifdef HAVE_XCACHE_DPRINT
3150/* {{{ proto bool  xcache_dprint(mixed value)
3151   Prints variable (or value) internal struct (debug only) */
3152PHP_FUNCTION(xcache_dprint)
3153{
3154    zval *value;
3155
3156    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3157        return;
3158    }
3159    xc_dprint_zval(value, 0 TSRMLS_CC);
3160}
3161/* }}} */
3162#endif
3163/* {{{ proto string xcache_asm(string filename)
3164 */
3165#ifdef HAVE_XCACHE_ASSEMBLER
3166PHP_FUNCTION(xcache_asm)
3167{
3168}
3169#endif
3170/* }}} */
3171#ifdef HAVE_XCACHE_DISASSEMBLER
3172/* {{{ proto array  xcache_dasm_file(string filename)
3173   Disassemble file into opcode array by filename */
3174PHP_FUNCTION(xcache_dasm_file)
3175{
3176    char *filename;
3177    int filename_len;
3178
3179    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
3180        return;
3181    }
3182    if (!filename_len) RETURN_FALSE;
3183
3184    xc_dasm_file(return_value, filename TSRMLS_CC);
3185}
3186/* }}} */
3187/* {{{ proto array  xcache_dasm_string(string code)
3188   Disassemble php code into opcode array */
3189PHP_FUNCTION(xcache_dasm_string)
3190{
3191    zval *code;
3192
3193    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
3194        return;
3195    }
3196    xc_dasm_string(return_value, code TSRMLS_CC);
3197}
3198/* }}} */
3199#endif
3200/* {{{ proto string xcache_encode(string filename)
3201   Encode php file into XCache opcode encoded format */
3202#ifdef HAVE_XCACHE_ENCODER
3203PHP_FUNCTION(xcache_encode)
3204{
3205}
3206#endif
3207/* }}} */
3208/* {{{ proto bool xcache_decode_file(string filename)
3209   Decode(load) opcode from XCache encoded format file */
3210#ifdef HAVE_XCACHE_DECODER
3211PHP_FUNCTION(xcache_decode_file)
3212{
3213}
3214#endif
3215/* }}} */
3216/* {{{ proto bool xcache_decode_string(string data)
3217   Decode(load) opcode from XCache encoded format data */
3218#ifdef HAVE_XCACHE_DECODER
3219PHP_FUNCTION(xcache_decode_string)
3220{
3221}
3222#endif
3223/* }}} */
3224/* {{{ xc_call_getter */
3225typedef const char *(xc_name_getter_t)(zend_uchar type);
3226static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
3227{
3228    long spec;
3229    const char *name;
3230
3231    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3232        return;
3233    }
3234    if (spec >= 0 && spec < count) {
3235        name = getter((zend_uchar) spec);
3236        if (name) {
3237            /* RETURN_STRING */
3238            int len = strlen(name);
3239            return_value->value.str.len = len;
3240            return_value->value.str.val = estrndup(name, len);
3241            return_value->type = IS_STRING; 
3242            return;
3243        }
3244    }
3245    RETURN_NULL();
3246}
3247/* }}} */
3248/* {{{ proto string xcache_get_op_type(int op_type) */
3249PHP_FUNCTION(xcache_get_op_type)
3250{
3251    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3252}
3253/* }}} */
3254/* {{{ proto string xcache_get_data_type(int type) */
3255PHP_FUNCTION(xcache_get_data_type)
3256{
3257    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3258}
3259/* }}} */
3260/* {{{ proto string xcache_get_opcode(int opcode) */
3261PHP_FUNCTION(xcache_get_opcode)
3262{
3263    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3264}
3265/* }}} */
3266/* {{{ proto string xcache_get_op_spec(int op_type) */
3267PHP_FUNCTION(xcache_get_op_spec)
3268{
3269    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3270}
3271/* }}} */
3272#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
3273/* {{{ proto string xcache_get_opcode_spec(int opcode) */
3274PHP_FUNCTION(xcache_get_opcode_spec)
3275{
3276    long spec;
3277    const xc_opcode_spec_t *opspec;
3278
3279    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3280        return;
3281    }
3282    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
3283        opspec = xc_get_opcode_spec((zend_uchar) spec);
3284        if (opspec) {
3285            array_init(return_value);
3286            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
3287            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
3288            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
3289            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
3290            return;
3291        }
3292    }
3293    RETURN_NULL();
3294}
3295/* }}} */
3296#endif
3297/* {{{ proto mixed xcache_get_special_value(zval value)
3298   XCache internal use only: For decompiler to get static value with type fixed */
3299PHP_FUNCTION(xcache_get_special_value)
3300{
3301    zval *value;
3302
3303    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3304        return;
3305    }
3306
3307    switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
3308    case IS_CONSTANT:
3309        *return_value = *value;
3310        zval_copy_ctor(return_value);
3311        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
3312        break;
3313
3314    case IS_CONSTANT_ARRAY:
3315        *return_value = *value;
3316        zval_copy_ctor(return_value);
3317        return_value->type = IS_ARRAY;
3318        break;
3319
3320    default:
3321        RETURN_NULL();
3322    }
3323}
3324/* }}} */
3325/* {{{ proto int xcache_get_type(zval value)
3326   XCache internal use only for disassembler to get variable type in engine level */
3327PHP_FUNCTION(xcache_get_type)
3328{
3329    zval *value;
3330
3331    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3332        return;
3333    }
3334
3335    RETURN_LONG(Z_TYPE_P(value));
3336}
3337/* }}} */
3338/* {{{ proto string xcache_coredump(int op_type) */
3339PHP_FUNCTION(xcache_coredump)
3340{
3341    if (xc_test) {
3342        raise(SIGSEGV);
3343    }
3344    else {
3345        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
3346    }
3347}
3348/* }}} */
3349/* {{{ proto string xcache_is_autoglobal(string name) */
3350PHP_FUNCTION(xcache_is_autoglobal)
3351{
3352    zval *name;
3353
3354    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
3355        return;
3356    }
3357
3358#ifdef IS_UNICODE
3359    convert_to_unicode(name);
3360#else
3361    convert_to_string(name);
3362#endif
3363
3364    RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
3365}
3366/* }}} */
3367static zend_function_entry xcache_functions[] = /* {{{ */
3368{
3369    PHP_FE(xcache_count,             NULL)
3370    PHP_FE(xcache_info,              NULL)
3371    PHP_FE(xcache_list,              NULL)
3372    PHP_FE(xcache_clear_cache,       NULL)
3373    PHP_FE(xcache_coredump,          NULL)
3374#ifdef HAVE_XCACHE_ASSEMBLER
3375    PHP_FE(xcache_asm,               NULL)
3376#endif
3377#ifdef HAVE_XCACHE_DISASSEMBLER
3378    PHP_FE(xcache_dasm_file,         NULL)
3379    PHP_FE(xcache_dasm_string,       NULL)
3380#endif
3381#ifdef HAVE_XCACHE_ENCODER
3382    PHP_FE(xcache_encode,            NULL)
3383#endif
3384#ifdef HAVE_XCACHE_DECODER
3385    PHP_FE(xcache_decode_file,       NULL)
3386    PHP_FE(xcache_decode_string,     NULL)
3387#endif
3388#ifdef HAVE_XCACHE_COVERAGER
3389    PHP_FE(xcache_coverager_decode,  NULL)
3390    PHP_FE(xcache_coverager_start,   NULL)
3391    PHP_FE(xcache_coverager_stop,    NULL)
3392    PHP_FE(xcache_coverager_get,     NULL)
3393#endif
3394    PHP_FE(xcache_get_special_value, NULL)
3395    PHP_FE(xcache_get_type,          NULL)
3396    PHP_FE(xcache_get_op_type,       NULL)
3397    PHP_FE(xcache_get_data_type,     NULL)
3398    PHP_FE(xcache_get_opcode,        NULL)
3399#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
3400    PHP_FE(xcache_get_opcode_spec,   NULL)
3401#endif
3402    PHP_FE(xcache_is_autoglobal,     NULL)
3403    PHP_FE(xcache_inc,               NULL)
3404    PHP_FE(xcache_dec,               NULL)
3405    PHP_FE(xcache_get,               NULL)
3406    PHP_FE(xcache_set,               NULL)
3407    PHP_FE(xcache_isset,             NULL)
3408    PHP_FE(xcache_unset,             NULL)
3409    PHP_FE(xcache_unset_by_prefix,   NULL)
3410    PHP_FE(xcache_get_refcount,      NULL)
3411    PHP_FE(xcache_get_isref,         arginfo_xcache_get_isref)
3412#ifdef HAVE_XCACHE_DPRINT
3413    PHP_FE(xcache_dprint,            NULL)
3414#endif
3415    {NULL, NULL,                     NULL}
3416};
3417/* }}} */
3418
3419#ifdef ZEND_WIN32
3420#include "dbghelp.h"
3421typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
3422        CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
3423        CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
3424        CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
3425        );
3426
3427static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
3428static HMODULE dbghelpModule = NULL;
3429static char crash_dumpPath[_MAX_PATH] = { 0 };
3430static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
3431
3432static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
3433{
3434    HANDLE fileHandle;
3435
3436    SetUnhandledExceptionFilter(oldFilter);
3437
3438    /* create the file */
3439    fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3440
3441    if (fileHandle != INVALID_HANDLE_VALUE) {
3442        MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
3443        BOOL ok;
3444
3445        exceptionInformation.ThreadId = GetCurrentThreadId();
3446        exceptionInformation.ExceptionPointers = pExceptionInfo;
3447        exceptionInformation.ClientPointers = FALSE;
3448
3449        /* write the dump */
3450        ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory, &exceptionInformation, NULL, NULL);
3451        CloseHandle(fileHandle);
3452        if (ok) {
3453            zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
3454            return EXCEPTION_EXECUTE_HANDLER;
3455        }
3456        else {
3457            zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
3458        }
3459    }
3460    else {
3461        zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
3462    }
3463
3464    return EXCEPTION_CONTINUE_SEARCH;
3465}
3466/* }}} */
3467
3468static void xcache_restore_crash_handler() /* {{{ */
3469{
3470    if (oldFilter) {
3471        SetUnhandledExceptionFilter(oldFilter);
3472        oldFilter = NULL;
3473    }
3474}
3475/* }}} */
3476static void xcache_init_crash_handler() /* {{{ */
3477{
3478    /* firstly see if dbghelp.dll is around and has the function we need
3479       look next to the EXE first, as the one in System32 might be old
3480       (e.g. Windows 2000) */
3481    char dbghelpPath[_MAX_PATH];
3482
3483    if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
3484        char *slash = strchr(dbghelpPath, '\\');
3485        if (slash) {
3486            strcpy(slash + 1, "DBGHELP.DLL");
3487            dbghelpModule = LoadLibrary(dbghelpPath);
3488        }
3489    }
3490
3491    if (!dbghelpModule) {
3492        /* load any version we can */
3493        dbghelpModule = LoadLibrary("DBGHELP.DLL");
3494        if (!dbghelpModule) {
3495            zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
3496            return;
3497        }
3498    }
3499
3500    dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
3501    if (!dbghelp_MiniDumpWriteDump) {
3502        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");
3503        return;
3504    }
3505
3506#ifdef XCACHE_VERSION_REVISION
3507#define REVISION "r" XCACHE_VERSION_REVISION
3508#else
3509#define REVISION ""
3510#endif
3511    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());
3512#undef REVISION
3513
3514    oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
3515}
3516/* }}} */
3517#else
3518/* old signal handlers {{{ */
3519typedef void (*xc_sighandler_t)(int);
3520#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
3521#include "foreachcoresig.h"
3522#undef FOREACH_SIG
3523/* }}} */
3524static void xcache_signal_handler(int sig);
3525static void xcache_restore_crash_handler() /* {{{ */
3526{
3527#define FOREACH_SIG(sig) do { \
3528    if (old_##sig##_handler != xcache_signal_handler) { \
3529        signal(sig, old_##sig##_handler); \
3530    } \
3531    else { \
3532        signal(sig, SIG_DFL); \
3533    } \
3534} while (0)
3535#include "foreachcoresig.h"
3536#undef FOREACH_SIG
3537}
3538/* }}} */
3539static void xcache_init_crash_handler() /* {{{ */
3540{
3541#define FOREACH_SIG(sig) \
3542    old_##sig##_handler = signal(sig, xcache_signal_handler)
3543#include "foreachcoresig.h"
3544#undef FOREACH_SIG
3545}
3546/* }}} */
3547static void xcache_signal_handler(int sig) /* {{{ */
3548{
3549    xcache_restore_crash_handler();
3550    if (xc_coredump_dir && xc_coredump_dir[0]) {
3551        if (chdir(xc_coredump_dir) != 0) {
3552            /* error, but nothing can do about it
3553             * and should'nt print anything which might SEGV again */
3554        }
3555    }
3556    raise(sig);
3557}
3558/* }}} */
3559#endif
3560
3561/* {{{ PHP_INI */
3562
3563static PHP_INI_MH(xc_OnUpdateDummy)
3564{
3565    return SUCCESS;
3566}
3567
3568static PHP_INI_MH(xc_OnUpdateULong)
3569{
3570    zend_ulong *p = (zend_ulong *) mh_arg1;
3571
3572    *p = (zend_ulong) atoi(new_value);
3573    return SUCCESS;
3574}
3575
3576static PHP_INI_MH(xc_OnUpdateBool)
3577{
3578    zend_bool *p = (zend_bool *)mh_arg1;
3579
3580    if (strncasecmp("on", new_value, sizeof("on"))) {
3581        *p = (zend_bool) atoi(new_value);
3582    }
3583    else {
3584        *p = (zend_bool) 1;
3585    }
3586    return SUCCESS;
3587}
3588
3589static PHP_INI_MH(xc_OnUpdateString)
3590{
3591    char **p = (char**)mh_arg1;
3592    if (*p) {
3593        pefree(*p, 1);
3594    }
3595    *p = pemalloc(strlen(new_value) + 1, 1);
3596    strcpy(*p, new_value);
3597    return SUCCESS;
3598}
3599
3600#ifndef ZEND_ENGINE_2
3601#define OnUpdateLong OnUpdateInt
3602#endif
3603
3604#ifdef ZEND_WIN32
3605#   define DEFAULT_PATH "xcache"
3606#else
3607#   define DEFAULT_PATH "/dev/zero"
3608#endif
3609PHP_INI_BEGIN()
3610    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
3611    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
3612    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
3613    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
3614    /* opcode cache */
3615    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3616    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3617    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3618    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
3619    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
3620    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
3621    /* var cache */
3622    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3623    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3624    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3625    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
3626    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
3627
3628    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
3629    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
3630    STD_PHP_INI_BOOLEAN("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, OnUpdateBool,        auth_enabled,      zend_xcache_globals, xcache_globals)
3631    STD_PHP_INI_BOOLEAN("xcache.experimental",           "0", PHP_INI_ALL,    OnUpdateBool,        experimental,      zend_xcache_globals, xcache_globals)
3632#ifdef HAVE_XCACHE_OPTIMIZER
3633    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
3634#endif
3635    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
3636#ifdef HAVE_XCACHE_COVERAGER
3637    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
3638    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
3639#endif
3640PHP_INI_END()
3641/* }}} */
3642/* {{{ PHP_MINFO_FUNCTION(xcache) */
3643static PHP_MINFO_FUNCTION(xcache)
3644{
3645    char buf[100];
3646    char *ptr;
3647    int left, len;
3648    xc_shm_scheme_t *scheme;
3649#ifdef HAVE_XCACHE_COVERAGER
3650    char *covdumpdir;
3651#endif
3652
3653    php_info_print_table_start();
3654    php_info_print_table_header(2, "XCache Support", "enabled");
3655    php_info_print_table_row(2, "Version", XCACHE_VERSION);
3656#ifdef XCACHE_VERSION_REVISION
3657    php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
3658#endif
3659    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
3660    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
3661#ifdef ZEND_ENGINE_2_1
3662    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
3663    php_info_print_table_row(2, "Cache Init Time", ptr);
3664    efree(ptr);
3665#else
3666    snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
3667    php_info_print_table_row(2, "Cache Init Time", buf);
3668#endif
3669
3670#ifdef ZTS
3671    snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
3672#else
3673    snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
3674#endif
3675    php_info_print_table_row(2, "Cache Instance Id", buf);
3676
3677    if (xc_php_size) {
3678        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
3679        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
3680        php_info_print_table_row(2, "Opcode Cache", buf);
3681        efree(ptr);
3682    }
3683    else {
3684        php_info_print_table_row(2, "Opcode Cache", "disabled");
3685    }
3686    if (xc_var_size) {
3687        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
3688        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
3689        php_info_print_table_row(2, "Variable Cache", buf);
3690        efree(ptr);
3691    }
3692    else {
3693        php_info_print_table_row(2, "Variable Cache", "disabled");
3694    }
3695
3696    left = sizeof(buf);
3697    ptr = buf;
3698    buf[0] = '\0';
3699    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
3700        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
3701        left -= len;
3702        ptr += len;
3703    }
3704    php_info_print_table_row(2, "Shared Memory Schemes", buf);
3705
3706#ifdef HAVE_XCACHE_COVERAGER
3707    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
3708        covdumpdir = NULL;
3709    }
3710    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
3711#endif
3712    php_info_print_table_end();
3713
3714    DISPLAY_INI_ENTRIES();
3715}
3716/* }}} */
3717/* {{{ extension startup */
3718static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
3719{
3720    zend_extension extension;
3721
3722    extension = *new_extension;
3723    extension.handle = handle;
3724
3725    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
3726
3727    zend_llist_prepend_element(&zend_extensions, &extension);
3728    TRACE("%s", "registered");
3729}
3730
3731static zend_llist_element *xc_llist_get_element_by_zend_extension(zend_llist *l, const char *extension_name)
3732{
3733    zend_llist_element *element;
3734
3735    for (element = zend_extensions.head; element; element = element->next) {
3736        zend_extension *extension = (zend_extension *) element->data;
3737
3738        if (!strcmp(extension->name, extension_name)) {
3739            return element;
3740        }
3741    }
3742    return NULL;
3743}
3744
3745static void xc_llist_prepend(zend_llist *l, zend_llist_element *element)
3746{
3747    element->next = l->head;
3748    element->prev = NULL;
3749    if (l->head) {
3750        l->head->prev = element;
3751    }
3752    else {
3753        l->tail = element;
3754    }
3755    l->head = element;
3756    ++l->count;
3757}
3758
3759static void xc_llist_unlink(zend_llist *l, zend_llist_element *element)
3760{
3761    if ((element)->prev) {
3762        (element)->prev->next = (element)->next;
3763    }
3764    else {
3765        (l)->head = (element)->next;
3766    }
3767
3768    if ((element)->next) {
3769        (element)->next->prev = (element)->prev;
3770    }
3771    else {
3772        (l)->tail = (element)->prev;
3773    }
3774
3775    --l->count;
3776}
3777
3778static int xc_zend_extension_startup(zend_extension *extension)
3779{
3780    if (extension->startup) {
3781        if (extension->startup(extension) != SUCCESS) {
3782            return FAILURE;
3783        }
3784    }
3785    return SUCCESS;
3786}
3787/* }}} */
3788static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
3789{
3790    return p1 == p2;
3791}
3792/* }}} */
3793static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
3794{
3795    llist_dtor_func_t dtor;
3796
3797    assert(extension);
3798    dtor = zend_extensions.dtor; /* avoid dtor */
3799    zend_extensions.dtor = NULL;
3800    zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
3801    zend_extensions.dtor = dtor;
3802    return SUCCESS;
3803}
3804/* }}} */
3805static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
3806{
3807    size_t bits, size;
3808    char *value;
3809
3810    if (cfg_get_string(name, &value) != SUCCESS) {
3811        value = default_value;
3812    }
3813
3814    p->size = zend_atoi(value, strlen(value));
3815    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
3816        /* empty body */
3817    }
3818    p->size = size;
3819    p->bits = bits;
3820    p->mask = size - 1;
3821
3822    return SUCCESS;
3823}
3824/* }}} */
3825static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
3826{
3827    char *value;
3828
3829    if (cfg_get_string(name, &value) != SUCCESS) {
3830        value = default_value;
3831    }
3832
3833    *p = zend_atol(value, strlen(value));
3834    return SUCCESS;
3835}
3836/* }}} */
3837/* {{{ PHP_MINIT_FUNCTION(xcache) */
3838static PHP_MINIT_FUNCTION(xcache)
3839{
3840    char *env;
3841    zend_extension *ext;
3842    zend_llist_position lpos;
3843
3844    xc_module_gotup = 1;
3845    if (!xc_zend_extension_gotup) {
3846        zend_error(E_WARNING, "XCache is designed to be loaded as zend_extension not extension");
3847        xc_zend_extension_register(&zend_extension_entry, 0);
3848        xc_zend_extension_startup(&zend_extension_entry);
3849        xc_zend_extension_faked = 1;
3850    }
3851
3852    ext = zend_get_extension("Zend Optimizer");
3853    if (ext) {
3854        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
3855        ext->op_array_handler = NULL;
3856    }
3857    /* cache if there's an op_array_ctor */
3858    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
3859            ext;
3860            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
3861        if (ext->op_array_ctor) {
3862            xc_have_op_array_ctor = 1;
3863            break;
3864        }
3865    }
3866
3867
3868#ifndef PHP_GINIT
3869    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
3870#endif
3871    REGISTER_INI_ENTRIES();
3872
3873    /* additional_functions requires PHP 5.3. TODO: find simpler way to do it */
3874#ifdef ZEND_ENGINE_2_3
3875    if (strcmp(sapi_module.name, "cgi-fcgi") == 0 && !sapi_module.additional_functions && !getenv("XCACHE_SKIP_FCGI_WARNING") && !getenv("GATEWAY_INTERFACE")) {
3876        if ((getenv("PHP_FCGI_CHILDREN") == NULL) || (atoi(getenv("PHP_FCGI_CHILDREN")) < 1)) {
3877            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");
3878        }
3879    }
3880#endif
3881
3882    xc_config_long(&xc_php_size,       "xcache.size",        "0");
3883    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
3884    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
3885
3886    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
3887    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
3888    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
3889
3890    if (strcmp(sapi_module.name, "cli") == 0) {
3891        if ((env = getenv("XCACHE_TEST")) != NULL) {
3892            xc_test = atoi(env);
3893        }
3894        if (!xc_test) {
3895            /* disable cache for cli except for testing */
3896            xc_php_size = xc_var_size = 0;
3897        }
3898    }
3899
3900    if (xc_php_size <= 0) {
3901        xc_php_size = xc_php_hcache.size = 0;
3902    }
3903    if (xc_var_size <= 0) {
3904        xc_var_size = xc_var_hcache.size = 0;
3905    }
3906
3907    if (xc_coredump_dir && xc_coredump_dir[0]) {
3908        xcache_init_crash_handler();
3909    }
3910
3911    xc_init_constant(module_number TSRMLS_CC);
3912    xc_shm_init_modules();
3913
3914    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
3915        if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
3916            zend_error(E_ERROR, "XCache: Cannot init");
3917            goto err_init;
3918        }
3919        xc_initized = 1;
3920        xc_init_time = time(NULL);
3921#ifdef PHP_WIN32
3922        xc_init_instance_id = GetCurrentProcessId();
3923#else
3924        xc_init_instance_id = getpid();
3925#endif
3926#ifdef ZTS
3927        xc_init_instance_subid = tsrm_thread_id();
3928#endif
3929    }
3930
3931    xc_util_init(module_number TSRMLS_CC);
3932#ifdef HAVE_XCACHE_COVERAGER
3933    xc_coverager_init(module_number TSRMLS_CC);
3934#endif
3935
3936    return SUCCESS;
3937
3938err_init:
3939    return FAILURE;
3940}
3941/* }}} */
3942/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
3943static PHP_MSHUTDOWN_FUNCTION(xcache)
3944{
3945#ifdef HAVE_XCACHE_COVERAGER
3946    xc_coverager_destroy();
3947#endif
3948    xc_util_destroy();
3949
3950    if (xc_initized) {
3951        xc_destroy();
3952    }
3953    if (xc_mmap_path) {
3954        pefree(xc_mmap_path, 1);
3955        xc_mmap_path = NULL;
3956    }
3957    if (xc_shm_scheme) {
3958        pefree(xc_shm_scheme, 1);
3959        xc_shm_scheme = NULL;
3960    }
3961
3962    if (xc_coredump_dir && xc_coredump_dir[0]) {
3963        xcache_restore_crash_handler();
3964    }
3965    if (xc_coredump_dir) {
3966        pefree(xc_coredump_dir, 1);
3967        xc_coredump_dir = NULL;
3968    }
3969#ifndef PHP_GINIT
3970#   ifdef ZTS
3971    ts_free_id(xcache_globals_id);
3972#   else
3973    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
3974#   endif
3975#endif
3976
3977    if (xc_zend_extension_faked) {
3978        zend_extension *ext = zend_get_extension(XCACHE_NAME);
3979        if (ext) {
3980            if (ext->shutdown) {
3981                ext->shutdown(ext);
3982            }
3983            xc_zend_remove_extension(ext);
3984        }
3985    }
3986    UNREGISTER_INI_ENTRIES();
3987
3988    xc_module_gotup = 0;
3989    xc_zend_extension_gotup = 0;
3990    xc_zend_extension_faked = 0;
3991
3992    return SUCCESS;
3993}
3994/* }}} */
3995/* {{{ PHP_RINIT_FUNCTION(xcache) */
3996static PHP_RINIT_FUNCTION(xcache)
3997{
3998    xc_request_init(TSRMLS_C);
3999    return SUCCESS;
4000}
4001/* }}} */
4002/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
4003#ifndef ZEND_ENGINE_2
4004static PHP_RSHUTDOWN_FUNCTION(xcache)
4005#else
4006static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
4007#endif
4008{
4009#ifdef ZEND_ENGINE_2
4010    TSRMLS_FETCH();
4011#endif
4012
4013    xc_request_shutdown(TSRMLS_C);
4014    return SUCCESS;
4015}
4016/* }}} */
4017/* {{{ module dependencies */
4018#if ZEND_MODULE_API_NO >= 20050922
4019static zend_module_dep xcache_module_deps[] = {
4020    ZEND_MOD_REQUIRED("standard")
4021    ZEND_MOD_CONFLICTS("apc")
4022    ZEND_MOD_CONFLICTS("eAccelerator")
4023    ZEND_MOD_CONFLICTS("Turck MMCache")
4024    {NULL, NULL, NULL}
4025};
4026#endif
4027/* }}} */ 
4028/* {{{ module definition structure */
4029
4030zend_module_entry xcache_module_entry = {
4031#if ZEND_MODULE_API_NO >= 20050922
4032    STANDARD_MODULE_HEADER_EX,
4033    NULL,
4034    xcache_module_deps,
4035#else
4036    STANDARD_MODULE_HEADER,
4037#endif
4038    XCACHE_NAME,
4039    xcache_functions,
4040    PHP_MINIT(xcache),
4041    PHP_MSHUTDOWN(xcache),
4042    PHP_RINIT(xcache),
4043#ifndef ZEND_ENGINE_2
4044    PHP_RSHUTDOWN(xcache),
4045#else
4046    NULL,
4047#endif
4048    PHP_MINFO(xcache),
4049    XCACHE_VERSION,
4050#ifdef PHP_GINIT
4051    PHP_MODULE_GLOBALS(xcache),
4052    PHP_GINIT(xcache),
4053    PHP_GSHUTDOWN(xcache),
4054#endif
4055#ifdef ZEND_ENGINE_2
4056    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
4057#else
4058    NULL,
4059    NULL,
4060#endif
4061    STANDARD_MODULE_PROPERTIES_EX
4062};
4063
4064#ifdef COMPILE_DL_XCACHE
4065ZEND_GET_MODULE(xcache)
4066#endif
4067/* }}} */
4068static startup_func_t xc_last_ext_startup;
4069static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
4070{
4071    zend_extension *ext = zend_get_extension(XCACHE_NAME);
4072    if (ext) {
4073        zend_error(E_WARNING, "Module '" XCACHE_NAME "' already loaded");
4074    }
4075    /* restore */
4076    extension->startup = xc_last_ext_startup;
4077    if (extension->startup) {
4078        if (extension->startup(extension) != SUCCESS) {
4079            return FAILURE;
4080        }
4081    }
4082    assert(xc_llist_zend_extension);
4083    xc_llist_prepend(&zend_extensions, xc_llist_zend_extension);
4084    if (!xc_module_gotup) {
4085        return zend_startup_module(&xcache_module_entry);
4086    }
4087    return SUCCESS;
4088}
4089/* }}} */
4090ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
4091{
4092    xc_zend_extension_gotup = 1;
4093
4094    if (!origin_compile_file) {
4095        origin_compile_file = zend_compile_file;
4096        zend_compile_file = xc_check_initial_compile_file;
4097    }
4098
4099    if (zend_llist_count(&zend_extensions) > 1) {
4100        zend_llist_position lpos;
4101        zend_extension *ext;
4102
4103        xc_llist_zend_extension = xc_llist_get_element_by_zend_extension(&zend_extensions, XCACHE_NAME);
4104        if (xc_llist_zend_extension != zend_extensions.head) {
4105            zend_error(E_WARNING, "XCache must be loaded as the first zend_extension for maximum compatibility");
4106        }
4107        /* hide myself */
4108        xc_llist_unlink(&zend_extensions, xc_llist_zend_extension);
4109
4110        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
4111        assert(ext && ext != (zend_extension *) xc_llist_zend_extension->data);
4112        xc_last_ext_startup = ext->startup;
4113        ext->startup = xc_zend_startup_last;
4114    }
4115    else if (!xc_module_gotup) {
4116        return zend_startup_module(&xcache_module_entry);
4117    }
4118    return SUCCESS;
4119}
4120/* }}} */
4121ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
4122{
4123    /* empty */
4124}
4125/* }}} */
4126ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
4127{
4128#ifdef HAVE_XCACHE_COVERAGER
4129    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
4130#endif
4131}
4132/* }}} */
4133ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
4134{
4135#if 0
4136    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
4137#endif
4138}
4139/* }}} */
4140ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
4141{
4142#if 0
4143    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
4144#endif
4145}
4146/* }}} */
4147/* {{{ zend extension definition structure */
4148ZEND_DLEXPORT zend_extension zend_extension_entry = {
4149    XCACHE_NAME,
4150    XCACHE_VERSION,
4151    XCACHE_AUTHOR,
4152    XCACHE_URL,
4153    XCACHE_COPYRIGHT,
4154    xcache_zend_startup,
4155    xcache_zend_shutdown,
4156    NULL,           /* activate_func_t */
4157    NULL,           /* deactivate_func_t */
4158    NULL,           /* message_handler_func_t */
4159#ifdef HAVE_XCACHE_OPTIMIZER
4160    xc_optimizer_op_array_handler,
4161#else
4162    NULL,           /* op_array_handler_func_t */
4163#endif
4164    xcache_statement_handler,
4165    xcache_fcall_begin_handler,
4166    xcache_fcall_end_handler,
4167    NULL,           /* op_array_ctor_func_t */
4168    NULL,           /* op_array_dtor_func_t */
4169    STANDARD_ZEND_EXTENSION_PROPERTIES
4170};
4171
4172#ifndef ZEND_EXT_API
4173#   define ZEND_EXT_API ZEND_DLEXPORT
4174#endif
4175#if COMPILE_DL_XCACHE
4176ZEND_EXTENSION();
4177#endif
4178/* }}} */
Note: See TracBrowser for help on using the repository browser.