source: trunk/xcache.c @ 983

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

kill warnings

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