source: trunk/xcache.c @ 864

Last change on this file since 864 was 864, checked in by moo, 3 years ago

handle opened_path in 1 place

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