source: trunk/xcache.c @ 986

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

split sandbox source

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