source: trunk/xcache.c @ 957

Last change on this file since 957 was 957, checked in by moo, 22 months ago

adds to open_files whenever old_compile_file is not called

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