source: trunk/xcache.c @ 851

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

more readability cache property. reduce memory footprint for data caching

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