source: trunk/xcache.c @ 854

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

remove type/cache/hvalue from cached copy to reduce memory usage a little bit

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