r5:
[php5-apc.git] / php_apc.c
diff --git a/php_apc.c b/php_apc.c
new file mode 100644 (file)
index 0000000..f7b7a7b
--- /dev/null
+++ b/php_apc.c
@@ -0,0 +1,991 @@
+/*
+  +----------------------------------------------------------------------+
+  | APC                                                                  |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2006 The PHP Group                                     |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
+  |          Rasmus Lerdorf <rasmus@php.net>                             |
+  +----------------------------------------------------------------------+
+
+   This software was contributed to PHP by Community Connect Inc. in 2002
+   and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
+   Future revisions and derivatives of this source code must acknowledge
+   Community Connect Inc. as the original contributor of this module by
+   leaving this note intact in the source code.
+
+   All other licensing and usage conditions are those of the PHP Group.
+
+ */
+
+/* $Id: php_apc.c,v 3.154 2007/12/26 22:31:20 rasmus Exp $ */
+
+#include "apc_zend.h"
+#include "apc_cache.h"
+#include "apc_main.h"
+#include "apc_sma.h"
+#include "apc_lock.h"
+#include "php_globals.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "SAPI.h"
+#include "rfc1867.h"
+#include "php_apc.h"
+#if PHP_API_VERSION <= 20020918
+#if HAVE_APACHE
+#ifdef APC_PHP4_STAT
+#undef XtOffsetOf
+#include "httpd.h"
+#endif
+#endif
+#endif
+
+#if HAVE_SIGACTION
+#include "apc_signal.h"
+#endif
+
+/* {{{ PHP_FUNCTION declarations */
+PHP_FUNCTION(apc_cache_info);
+PHP_FUNCTION(apc_clear_cache);
+PHP_FUNCTION(apc_sma_info);
+PHP_FUNCTION(apc_store);
+PHP_FUNCTION(apc_fetch);
+PHP_FUNCTION(apc_delete);
+PHP_FUNCTION(apc_compile_file);
+PHP_FUNCTION(apc_define_constants);
+PHP_FUNCTION(apc_load_constants);
+PHP_FUNCTION(apc_add);
+/* }}} */
+
+/* {{{ ZEND_DECLARE_MODULE_GLOBALS(apc) */
+ZEND_DECLARE_MODULE_GLOBALS(apc)
+
+/* True globals */
+apc_cache_t* apc_cache = NULL;       
+apc_cache_t* apc_user_cache = NULL;
+void* apc_compiled_filters = NULL;
+
+static void php_apc_init_globals(zend_apc_globals* apc_globals TSRMLS_DC)
+{
+    apc_globals->filters = NULL;
+    apc_globals->initialized = 0;
+    apc_globals->cache_stack = apc_stack_create(0);
+    apc_globals->cache_by_default = 1;
+    apc_globals->slam_defense = 0;
+    apc_globals->mem_size_ptr = NULL;
+    apc_globals->fpstat = 1;
+    apc_globals->stat_ctime = 0;
+    apc_globals->write_lock = 1;
+    apc_globals->report_autofilter = 0;
+    apc_globals->apc_optimize_function = NULL;
+#ifdef MULTIPART_EVENT_FORMDATA
+    apc_globals->rfc1867 = 0;
+#endif
+    apc_globals->copied_zvals = NULL;
+#ifdef ZEND_ENGINE_2
+    apc_globals->reserved_offset = -1;
+#endif
+    apc_globals->localcache = 0;
+    apc_globals->localcache_size = 0;
+    apc_globals->lcache = NULL;
+    apc_globals->force_file_update = 0;
+    apc_globals->coredump_unmap = 0;
+}
+
+static void php_apc_shutdown_globals(zend_apc_globals* apc_globals TSRMLS_DC)
+{
+    /* deallocate the ignore patterns */
+    if (apc_globals->filters != NULL) {
+        int i;
+        for (i=0; apc_globals->filters[i] != NULL; i++) {
+            apc_efree(apc_globals->filters[i]);
+        }
+        apc_efree(apc_globals->filters);
+    }
+
+    /* the stack should be empty */
+    assert(apc_stack_size(apc_globals->cache_stack) == 0); 
+
+    /* apc cleanup */
+    apc_stack_destroy(apc_globals->cache_stack);
+
+    /* the rest of the globals are cleaned up in apc_module_shutdown() */
+}
+
+/* }}} */
+
+/* {{{ PHP_INI */
+
+static PHP_INI_MH(OnUpdate_filters) /* {{{ */
+{
+    APCG(filters) = apc_tokenize(new_value, ',');
+    return SUCCESS;
+}
+/* }}} */
+
+static PHP_INI_MH(OnUpdateShmSegments) /* {{{ */
+{
+#if APC_MMAP
+    if(atoi(new_value)!=1) {
+        php_error_docref(NULL TSRMLS_CC, E_WARNING, "apc.shm_segments setting ignored in MMAP mode");
+    }
+    APCG(shm_segments) = 1; 
+#else
+    APCG(shm_segments) = atoi(new_value);
+#endif
+    return SUCCESS;
+}
+/* }}} */
+
+#ifdef MULTIPART_EVENT_FORMDATA
+static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */
+{
+    int tmp;
+    tmp = zend_atoi(new_value, new_value_length);
+    if(tmp < 0) {
+        apc_eprint("rfc1867_freq must be greater than or equal to zero.");
+        return FAILURE;
+    }
+    if(new_value[new_value_length-1] == '%') {
+        if(tmp > 100) {
+            apc_eprint("rfc1867_freq cannot be over 100%%");
+            return FAILURE;
+        }
+        APCG(rfc1867_freq) = tmp / 100.0;
+    } else {
+        APCG(rfc1867_freq) = tmp;
+    }
+    return SUCCESS;
+}
+/* }}} */
+#endif
+
+#ifdef ZEND_ENGINE_2
+#define OnUpdateInt OnUpdateLong
+#endif
+
+PHP_INI_BEGIN()
+STD_PHP_INI_BOOLEAN("apc.enabled",      "1",    PHP_INI_SYSTEM, OnUpdateBool,              enabled,         zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.shm_segments",   "1",    PHP_INI_SYSTEM, OnUpdateShmSegments,       shm_segments,    zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.shm_size",       "30",   PHP_INI_SYSTEM, OnUpdateInt,            shm_size,        zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.include_once_override", "0", PHP_INI_SYSTEM, OnUpdateBool,     include_once,    zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.num_files_hint", "1000", PHP_INI_SYSTEM, OnUpdateInt,            num_files_hint,  zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.user_entries_hint", "4096", PHP_INI_SYSTEM, OnUpdateInt,          user_entries_hint, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.gc_ttl",         "3600", PHP_INI_SYSTEM, OnUpdateInt,            gc_ttl,           zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.ttl",            "0",    PHP_INI_SYSTEM, OnUpdateInt,            ttl,              zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.user_ttl",       "0",    PHP_INI_SYSTEM, OnUpdateInt,            user_ttl,         zend_apc_globals, apc_globals)
+#if APC_MMAP
+STD_PHP_INI_ENTRY("apc.mmap_file_mask",  NULL,  PHP_INI_SYSTEM, OnUpdateString,         mmap_file_mask,   zend_apc_globals, apc_globals)
+#endif
+PHP_INI_ENTRY("apc.filters",        NULL,     PHP_INI_SYSTEM, OnUpdate_filters)
+STD_PHP_INI_BOOLEAN("apc.cache_by_default", "1",  PHP_INI_ALL, OnUpdateBool,         cache_by_default, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.slam_defense", "0",      PHP_INI_SYSTEM, OnUpdateInt,            slam_defense,     zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.file_update_protection", "2", PHP_INI_SYSTEM, OnUpdateInt,file_update_protection,  zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.enable_cli", "0",      PHP_INI_SYSTEM, OnUpdateBool,           enable_cli,       zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.max_file_size", "1M",    PHP_INI_SYSTEM, OnUpdateInt,            max_file_size,    zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.stat", "1",            PHP_INI_SYSTEM, OnUpdateBool,           fpstat,           zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.stat_ctime", "0",      PHP_INI_SYSTEM, OnUpdateBool,           stat_ctime,       zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.write_lock", "1",      PHP_INI_SYSTEM, OnUpdateBool,           write_lock,       zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.report_autofilter", "0", PHP_INI_SYSTEM, OnUpdateBool,         report_autofilter,zend_apc_globals, apc_globals)
+#ifdef MULTIPART_EVENT_FORMDATA
+STD_PHP_INI_BOOLEAN("apc.rfc1867", "0", PHP_INI_SYSTEM, OnUpdateBool, rfc1867, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.rfc1867_prefix", "upload_", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_prefix, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.rfc1867_name", "APC_UPLOAD_PROGRESS", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_name, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.rfc1867_freq", "0", PHP_INI_SYSTEM, OnUpdateRfc1867Freq, rfc1867_freq, zend_apc_globals, apc_globals)
+#endif
+STD_PHP_INI_BOOLEAN("apc.localcache", "0", PHP_INI_SYSTEM, OnUpdateBool, localcache, zend_apc_globals, apc_globals)
+STD_PHP_INI_ENTRY("apc.localcache.size", "512", PHP_INI_SYSTEM, OnUpdateInt, localcache_size,  zend_apc_globals, apc_globals)
+STD_PHP_INI_BOOLEAN("apc.coredump_unmap", "0", PHP_INI_SYSTEM, OnUpdateBool, coredump_unmap, zend_apc_globals, apc_globals)
+PHP_INI_END()
+
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION(apc) */
+static PHP_MINFO_FUNCTION(apc)
+{
+    php_info_print_table_start();
+    php_info_print_table_row(2, "APC Support", APCG(enabled) ? "enabled" : "disabled");
+    php_info_print_table_row(2, "Version", APC_VERSION);
+#if APC_MMAP
+    php_info_print_table_row(2, "MMAP Support", "Enabled");
+    php_info_print_table_row(2, "MMAP File Mask", APCG(mmap_file_mask));
+#else
+    php_info_print_table_row(2, "MMAP Support", "Disabled");
+#endif
+#if APC_SEM_LOCKS
+    php_info_print_table_row(2, "Locking type", "IPC Semaphore");
+#elif APC_FUTEX_LOCKS
+    php_info_print_table_row(2, "Locking type", "Linux Futex Locks");
+#elif APC_PTHREADMUTEX_LOCKS
+    php_info_print_table_row(2, "Locking type", "pthread mutex Locks");
+#elif APC_SPIN_LOCKS
+    php_info_print_table_row(2, "Locking type", "spin Locks");
+#else
+    php_info_print_table_row(2, "Locking type", "File Locks");
+#endif
+    php_info_print_table_row(2, "Revision", "$Revision: 3.154 $");
+    php_info_print_table_row(2, "Build Date", __DATE__ " " __TIME__);
+    php_info_print_table_end();
+    DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+#ifdef MULTIPART_EVENT_FORMDATA
+extern int apc_rfc1867_progress(unsigned int event, void *event_data, void **extra TSRMLS_DC);
+#endif
+
+/* {{{ PHP_MINIT_FUNCTION(apc) */
+static PHP_MINIT_FUNCTION(apc)
+{
+    ZEND_INIT_MODULE_GLOBALS(apc, php_apc_init_globals, php_apc_shutdown_globals);
+
+    REGISTER_INI_ENTRIES();
+
+    /* Disable APC in cli mode unless overridden by apc.enable_cli */
+    if(!APCG(enable_cli) && !strcmp(sapi_module.name, "cli")) {
+        APCG(enabled) = 0;
+    }
+
+    if (APCG(enabled)) {
+        if(APCG(initialized)) {
+            apc_process_init(module_number TSRMLS_CC);
+        } else {
+            apc_module_init(module_number TSRMLS_CC);
+            apc_zend_init(TSRMLS_C);
+            apc_process_init(module_number TSRMLS_CC);
+#ifdef MULTIPART_EVENT_FORMDATA
+            /* File upload progress tracking */
+            if(APCG(rfc1867)) {
+                php_rfc1867_callback = apc_rfc1867_progress;
+            }
+#endif
+        }
+    }
+
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION(apc) */
+static PHP_MSHUTDOWN_FUNCTION(apc)
+{
+    if(APCG(enabled)) {
+        apc_process_shutdown(TSRMLS_C);
+        apc_zend_shutdown(TSRMLS_C);
+        apc_module_shutdown(TSRMLS_C);
+#ifndef ZTS
+        php_apc_shutdown_globals(&apc_globals);
+#endif
+    }
+#ifdef ZTS
+    ts_free_id(apc_globals_id);
+#endif
+    UNREGISTER_INI_ENTRIES();
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RINIT_FUNCTION(apc) */
+static PHP_RINIT_FUNCTION(apc)
+{
+    if(APCG(enabled)) {
+        apc_request_init(TSRMLS_C);
+
+#if HAVE_SIGACTION
+        apc_set_signals();
+#endif
+    }
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION(apc) */
+static PHP_RSHUTDOWN_FUNCTION(apc)
+{
+    if(APCG(enabled)) {
+        apc_request_shutdown(TSRMLS_C);
+    }
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ proto array apc_cache_info([string type] [, bool limited]) */
+PHP_FUNCTION(apc_cache_info)
+{
+    apc_cache_info_t* info;
+    apc_cache_link_t* p;
+    zval* list;
+    char *cache_type;
+    int ct_len;
+    zend_bool limited=0;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &cache_type, &ct_len, &limited) == FAILURE) {
+        return;
+    }
+
+    if(ZEND_NUM_ARGS()) {
+        if(!strcasecmp(cache_type,"user")) {
+            info = apc_cache_info(apc_user_cache, limited);
+        } else if(!strcasecmp(cache_type,"filehits")) {
+#ifdef APC_FILEHITS
+            RETVAL_ZVAL(APCG(filehits), 1, 0);
+            return;
+#else
+            RETURN_FALSE;
+#endif
+        } else {
+            info = apc_cache_info(apc_cache, limited);
+        }
+    } else info = apc_cache_info(apc_cache, limited);
+
+    if(!info) {
+        php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC info available.  Perhaps APC is not enabled? Check apc.enabled in your ini file");
+        RETURN_FALSE;
+    }
+
+    array_init(return_value);
+    add_assoc_long(return_value, "num_slots", info->num_slots);
+    add_assoc_long(return_value, "ttl", info->ttl);
+    add_assoc_long(return_value, "num_hits", info->num_hits);
+    add_assoc_long(return_value, "num_misses", info->num_misses);
+    add_assoc_long(return_value, "start_time", info->start_time);
+    add_assoc_long(return_value, "expunges", info->expunges);
+    add_assoc_long(return_value, "mem_size", info->mem_size);
+    add_assoc_long(return_value, "num_entries", info->num_entries);
+    add_assoc_long(return_value, "num_inserts", info->num_inserts);
+#ifdef MULTIPART_EVENT_FORMDATA
+    add_assoc_long(return_value, "file_upload_progress", 1);
+#else
+    add_assoc_long(return_value, "file_upload_progress", 0);
+#endif
+#if APC_MMAP
+    add_assoc_stringl(return_value, "memory_type", "mmap", sizeof("mmap")-1, 1);
+#else
+    add_assoc_stringl(return_value, "memory_type", "IPC shared", sizeof("IPC shared")-1, 1);
+#endif
+#if APC_SEM_LOCKS
+    add_assoc_stringl(return_value, "locking_type", "IPC semaphore", sizeof("IPC semaphore")-1, 1);
+#elif APC_FUTEX_LOCKS
+    add_assoc_stringl(return_value, "locking_type", "Linux Futex", sizeof("Linux Futex")-1, 1);
+#elif APC_PTHREADMUTEX_LOCKS
+    add_assoc_stringl(return_value, "locking_type", "pthread mutex", sizeof("pthread mutex")-1, 1);
+#elif APC_SPIN_LOCKS
+    add_assoc_stringl(return_value, "locking_type", "spin", sizeof("spin")-1, 1);
+#else
+    add_assoc_stringl(return_value, "locking_type", "file", sizeof("file")-1, 1);
+#endif
+    if(limited) {
+        apc_cache_free_info(info);
+        return;
+    }
+    
+    ALLOC_INIT_ZVAL(list);
+    array_init(list);
+
+    for (p = info->list; p != NULL; p = p->next) {
+        zval* link;
+
+        ALLOC_INIT_ZVAL(link);
+        array_init(link);
+
+        if(p->type == APC_CACHE_ENTRY_FILE) {
+            add_assoc_string(link, "filename", p->data.file.filename, 1);
+            add_assoc_long(link, "device", p->data.file.device);
+            add_assoc_long(link, "inode", p->data.file.inode);
+            add_assoc_string(link, "type", "file", 1);
+        } else if(p->type == APC_CACHE_ENTRY_USER) {
+            add_assoc_string(link, "info", p->data.user.info, 1);
+            add_assoc_long(link, "ttl", (long)p->data.user.ttl);
+            add_assoc_string(link, "type", "user", 1);
+        }
+        add_assoc_long(link, "num_hits", p->num_hits);
+        add_assoc_long(link, "mtime", p->mtime);
+        add_assoc_long(link, "creation_time", p->creation_time);
+        add_assoc_long(link, "deletion_time", p->deletion_time);
+        add_assoc_long(link, "access_time", p->access_time);
+        add_assoc_long(link, "ref_count", p->ref_count);
+        add_assoc_long(link, "mem_size", p->mem_size);
+        add_next_index_zval(list, link);
+    }
+    add_assoc_zval(return_value, "cache_list", list);
+
+    ALLOC_INIT_ZVAL(list);
+    array_init(list);
+
+    for (p = info->deleted_list; p != NULL; p = p->next) {
+        zval* link;
+
+        ALLOC_INIT_ZVAL(link);
+        array_init(link);
+
+        if(p->type == APC_CACHE_ENTRY_FILE) {
+            add_assoc_string(link, "filename", p->data.file.filename, 1);
+            add_assoc_long(link, "device", p->data.file.device);
+            add_assoc_long(link, "inode", p->data.file.inode);
+            add_assoc_string(link, "type", "file", 1);
+        } else if(p->type == APC_CACHE_ENTRY_USER) {
+            add_assoc_string(link, "info", p->data.user.info, 1);
+            add_assoc_long(link, "ttl", (long)p->data.user.ttl);
+            add_assoc_string(link, "type", "user", 1);
+        }
+        add_assoc_long(link, "num_hits", p->num_hits);
+        add_assoc_long(link, "mtime", p->mtime);
+        add_assoc_long(link, "creation_time", p->creation_time);
+        add_assoc_long(link, "deletion_time", p->deletion_time);
+        add_assoc_long(link, "access_time", p->access_time);
+        add_assoc_long(link, "ref_count", p->ref_count);
+        add_assoc_long(link, "mem_size", p->mem_size);
+        add_next_index_zval(list, link);
+    }
+    add_assoc_zval(return_value, "deleted_list", list);
+
+    apc_cache_free_info(info);
+}
+/* }}} */
+
+/* {{{ proto void apc_clear_cache() */
+PHP_FUNCTION(apc_clear_cache)
+{
+    char *cache_type;
+    int ct_len;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache_type, &ct_len) == FAILURE) {
+        return;
+    }
+
+    if(ZEND_NUM_ARGS()) {
+        if(!strcasecmp(cache_type,"user")) {
+            apc_cache_clear(apc_user_cache);
+            RETURN_TRUE;
+        }
+    }
+    apc_cache_clear(apc_cache);
+}
+/* }}} */
+
+/* {{{ proto array apc_sma_info([bool limited]) */
+PHP_FUNCTION(apc_sma_info)
+{
+    apc_sma_info_t* info;
+    zval* block_lists;
+    int i;
+    zend_bool limited = 0;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &limited) == FAILURE) {
+        return;
+    }
+
+    info = apc_sma_info(limited);
+
+    if(!info) {
+        php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC SMA info available.  Perhaps APC is disabled via apc.enabled?");
+        RETURN_FALSE;
+    }
+
+    array_init(return_value);
+    add_assoc_long(return_value, "num_seg", info->num_seg);
+    add_assoc_long(return_value, "seg_size", info->seg_size);
+    add_assoc_long(return_value, "avail_mem", apc_sma_get_avail_mem());
+
+    if(limited) {
+        apc_sma_free_info(info);
+        return;
+    }
+
+#if ALLOC_DISTRIBUTION
+    {
+        size_t *adist = apc_sma_get_alloc_distribution();
+        zval* list;
+        ALLOC_INIT_ZVAL(list);
+        array_init(list);
+        for(i=0; i<30; i++) {
+            add_next_index_long(list, adist[i]);
+        }
+        add_assoc_zval(return_value, "adist", list);
+    }
+#endif
+    ALLOC_INIT_ZVAL(block_lists);
+    array_init(block_lists);
+
+    for (i = 0; i < info->num_seg; i++) {
+        apc_sma_link_t* p;
+        zval* list;
+
+        ALLOC_INIT_ZVAL(list);
+        array_init(list);
+
+        for (p = info->list[i]; p != NULL; p = p->next) {
+            zval* link;
+
+            ALLOC_INIT_ZVAL(link);
+            array_init(link);
+
+            add_assoc_long(link, "size", p->size);
+            add_assoc_long(link, "offset", p->offset);
+            add_next_index_zval(list, link);
+        }
+        add_next_index_zval(block_lists, list);
+    }
+    add_assoc_zval(return_value, "block_lists", block_lists);
+    apc_sma_free_info(info);
+}
+/* }}} */
+
+/* {{{ _apc_store */
+int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int ttl, const int exclusive TSRMLS_DC) {
+    apc_cache_entry_t *entry;
+    apc_cache_key_t key;
+    time_t t;
+    size_t mem_size = 0;
+
+#if PHP_API_VERSION < 20041225
+#if HAVE_APACHE && defined(APC_PHP4_STAT)
+    t = ((request_rec *)SG(server_context))->request_time;
+#else
+    t = time(0);
+#endif
+#else
+    t = sapi_get_request_time(TSRMLS_C);
+#endif
+
+    if(!APCG(enabled)) return 0;
+
+    HANDLE_BLOCK_INTERRUPTIONS();
+
+    APCG(mem_size_ptr) = &mem_size;
+    if (!(entry = apc_cache_make_user_entry(strkey, strkey_len + 1, val, ttl))) {
+        APCG(mem_size_ptr) = NULL;
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return 0;
+    }
+
+    if (!apc_cache_make_user_key(&key, strkey, strkey_len + 1, t)) {
+        APCG(mem_size_ptr) = NULL;
+        apc_cache_free_entry(entry);
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return 0;
+    }
+
+    if (!apc_cache_user_insert(apc_user_cache, key, entry, t, exclusive TSRMLS_CC)) {
+        APCG(mem_size_ptr) = NULL;
+        apc_cache_free_entry(entry);
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return 0;
+    }
+
+    APCG(mem_size_ptr) = NULL;
+
+    HANDLE_UNBLOCK_INTERRUPTIONS();
+
+    return 1;
+}
+/* }}} */
+
+/* {{{ proto int apc_store(string key, zval var [, ttl ])
+ */
+PHP_FUNCTION(apc_store) {
+    zval *val;
+    char *strkey;
+    int strkey_len;
+    long ttl = 0L;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) {
+        return;
+    }
+
+    if(!strkey_len) RETURN_FALSE;
+
+    if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 0 TSRMLS_CC)) RETURN_TRUE;
+    RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto int apc_add(string key, zval var [, ttl ])
+ */
+PHP_FUNCTION(apc_add) {
+    zval *val;
+    char *strkey;
+    int strkey_len;
+    long ttl = 0L;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) {
+        return;
+    }
+
+    if(!strkey_len) RETURN_FALSE;
+
+    if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 1 TSRMLS_CC)) RETURN_TRUE;
+    RETURN_FALSE;
+}
+/* }}} */
+
+void *apc_erealloc_wrapper(void *ptr, size_t size) {
+    return _erealloc(ptr, size, 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
+}
+
+/* {{{ RETURN_ZVAL for php4 */
+#if !defined(ZEND_ENGINE_2) && !defined(RETURN_ZVAL)
+#define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } 
+#define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
+#define ZVAL_ZVAL(z, zv, copy, dtor) {  \
+        int is_ref, refcount;           \
+        is_ref = (z)->is_ref;           \
+        refcount = (z)->refcount;       \
+        *(z) = *(zv);                   \
+        if (copy) {                     \
+            zval_copy_ctor(z);          \
+        }                               \
+        if (dtor) {                     \
+            if (!copy) {                \
+                ZVAL_NULL(zv);          \
+            }                           \
+            zval_ptr_dtor(&zv);         \
+        }                               \
+        (z)->is_ref = is_ref;           \
+        (z)->refcount = refcount;       \
+    }
+#endif
+/* }}} */
+
+/* {{{ proto mixed apc_fetch(mixed key)
+ */
+PHP_FUNCTION(apc_fetch) {
+    zval *key;
+    HashTable *hash;
+    HashPosition hpos;
+    zval **hentry;
+    zval *result;
+    zval *result_entry;
+    char *strkey;
+    int strkey_len;
+    apc_cache_entry_t* entry;
+    time_t t;
+
+    if(!APCG(enabled)) RETURN_FALSE;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) == FAILURE) {
+        return;
+    }
+
+#if PHP_API_VERSION < 20041225
+#if HAVE_APACHE && defined(APC_PHP4_STAT)
+    t = ((request_rec *)SG(server_context))->request_time;
+#else 
+    t = time(0);
+#endif
+#else
+    t = sapi_get_request_time(TSRMLS_C);
+#endif
+
+    if(Z_TYPE_P(key) != IS_STRING && Z_TYPE_P(key) != IS_ARRAY) {
+        convert_to_string(key);
+    }
+    
+    if(Z_TYPE_P(key) == IS_STRING) {
+        strkey = Z_STRVAL_P(key);
+        strkey_len = Z_STRLEN_P(key);
+        if(!strkey_len) RETURN_FALSE;
+        entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
+        if(entry) {
+            /* deep-copy returned shm zval to emalloc'ed return_value */
+            apc_cache_fetch_zval(return_value, entry->data.user.val, apc_php_malloc, apc_php_free);
+            apc_cache_release(apc_user_cache, entry);
+        } else {
+            RETURN_FALSE;
+        }
+    } else if(Z_TYPE_P(key) == IS_ARRAY) {
+        hash = Z_ARRVAL_P(key);
+        MAKE_STD_ZVAL(result);
+        array_init(result); 
+        zend_hash_internal_pointer_reset_ex(hash, &hpos);
+        while(zend_hash_get_current_data_ex(hash, (void**)&hentry, &hpos) == SUCCESS) {
+            if(Z_TYPE_PP(hentry) != IS_STRING) {
+                apc_wprint("apc_fetch() expects a string or array of strings.");
+                RETURN_FALSE;
+            }
+            entry = apc_cache_user_find(apc_user_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1, t);
+            if(entry) {
+                /* deep-copy returned shm zval to emalloc'ed return_value */
+                MAKE_STD_ZVAL(result_entry);
+                apc_cache_fetch_zval(result_entry, entry->data.user.val, apc_php_malloc, apc_php_free);
+                apc_cache_release(apc_user_cache, entry);
+                zend_hash_add(Z_ARRVAL_P(result), Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) +1, &result_entry, sizeof(zval*), NULL);
+            } /* don't set values we didn't find */
+            zend_hash_move_forward_ex(hash, &hpos);
+        }
+        RETURN_ZVAL(result, 0, 1);
+    } else {
+        apc_wprint("apc_fetch() expects a string or array of strings.");
+        RETURN_FALSE;
+    }
+
+    return;
+}
+/* }}} */
+
+/* {{{ proto mixed apc_delete(string key)
+ */
+PHP_FUNCTION(apc_delete) {
+    char *strkey;
+    int strkey_len;
+
+    if(!APCG(enabled)) RETURN_FALSE;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) {
+        return;
+    }
+
+    if(!strkey_len) RETURN_FALSE;
+
+    if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) {
+        RETURN_TRUE;
+    } else {
+        RETURN_FALSE;
+    }
+}
+/* }}} */
+
+static void _apc_define_constants(zval *constants, zend_bool case_sensitive TSRMLS_DC) {
+    char *const_key;
+    unsigned int const_key_len;
+    zval **entry;
+    HashPosition pos;
+
+    zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(constants), &pos);
+    while (zend_hash_get_current_data_ex(Z_ARRVAL_P(constants), (void**)&entry, &pos) == SUCCESS) {
+        zend_constant c;
+        int key_type;
+        ulong num_key;
+
+        key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(constants), &const_key, &const_key_len, &num_key, 0, &pos);
+        if(key_type != HASH_KEY_IS_STRING) {
+            zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
+            continue;
+        }
+        switch(Z_TYPE_PP(entry)) {
+            case IS_LONG:
+            case IS_DOUBLE:
+            case IS_STRING:
+            case IS_BOOL:
+            case IS_RESOURCE:
+            case IS_NULL:
+                break;
+            default:
+                zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
+                continue;
+        }
+        c.value = **entry;
+        zval_copy_ctor(&c.value);
+        c.flags = case_sensitive;
+        c.name = zend_strndup(const_key, const_key_len);
+        c.name_len = const_key_len;
+#ifdef ZEND_ENGINE_2
+        c.module_number = PHP_USER_CONSTANT;
+#endif
+        zend_register_constant(&c TSRMLS_CC);
+
+        zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
+    }
+}
+
+/* {{{ proto mixed apc_define_constants(string key, array constants [,bool case-sensitive])
+ */
+PHP_FUNCTION(apc_define_constants) {
+    char *strkey;
+    int strkey_len;
+    zval *constants = NULL;
+    zend_bool case_sensitive = 1;
+    int argc = ZEND_NUM_ARGS();
+
+    if (zend_parse_parameters(argc TSRMLS_CC, "sa|b", &strkey, &strkey_len, &constants, &case_sensitive) == FAILURE) {
+        return;
+    }
+
+    if(!strkey_len) RETURN_FALSE;
+
+    _apc_define_constants(constants, case_sensitive TSRMLS_CC);
+    if(_apc_store(strkey, strkey_len, constants, 0, 0 TSRMLS_CC)) RETURN_TRUE;
+    RETURN_FALSE;
+} /* }}} */
+
+/* {{{ proto mixed apc_load_constants(string key [, bool case-sensitive])
+ */
+PHP_FUNCTION(apc_load_constants) {
+    char *strkey;
+    int strkey_len;
+    apc_cache_entry_t* entry;
+    time_t t;
+    zend_bool case_sensitive = 1;
+
+    if(!APCG(enabled)) RETURN_FALSE;
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &strkey, &strkey_len, &case_sensitive) == FAILURE) {
+        return;
+    }
+
+    if(!strkey_len) RETURN_FALSE;
+
+#if PHP_API_VERSION < 20041225
+#if HAVE_APACHE && defined(APC_PHP4_STAT)
+    t = ((request_rec *)SG(server_context))->request_time;
+#else 
+    t = time(0);
+#endif
+#else 
+    t = sapi_get_request_time(TSRMLS_C);
+#endif
+
+    entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
+
+    if(entry) {
+        _apc_define_constants(entry->data.user.val, case_sensitive TSRMLS_CC);
+        apc_cache_release(apc_user_cache, entry);
+        RETURN_TRUE;
+    } else {
+        RETURN_FALSE;
+    }
+}
+/* }}} */
+
+/* {{{ proto boolean apc_compile_file(string filename)
+ */
+PHP_FUNCTION(apc_compile_file) {
+    char *filename;
+    int filename_len;
+    zend_file_handle file_handle;
+    zend_op_array *op_array;
+    long slam_defense = 0;
+    char** filters = NULL;
+    zend_bool cache_by_default = 1;
+    HashTable cg_function_table, cg_class_table, eg_function_table, eg_class_table;
+    HashTable *cg_orig_function_table, *cg_orig_class_table, *eg_orig_function_table, *eg_orig_class_table;
+
+    if(!APCG(enabled)) RETURN_FALSE;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+        return;
+    }
+
+    if(!filename) RETURN_FALSE;
+
+    /* reset slam defense, filters, and cache_by_default */
+    slam_defense = APCG(slam_defense);
+    APCG(slam_defense) = 0;
+   
+    filters = APCG(filters);
+    APCG(filters) = NULL;
+
+    cache_by_default = APCG(cache_by_default);
+    APCG(cache_by_default) = 1;
+
+    /* Replace function/class tables to avoid namespace conflicts */
+    zend_hash_init_ex(&cg_function_table, 100, NULL, ZEND_FUNCTION_DTOR, 1, 0);
+    cg_orig_function_table = CG(function_table);
+    CG(function_table) = &cg_function_table;
+    zend_hash_init_ex(&cg_class_table, 10, NULL, ZEND_CLASS_DTOR, 1, 0);
+    cg_orig_class_table = CG(class_table);
+    CG(class_table) = &cg_class_table;
+    eg_orig_function_table = EG(function_table);
+    EG(function_table) = CG(function_table);
+    eg_orig_class_table = EG(class_table);
+    EG(class_table) = CG(class_table);
+    APCG(force_file_update) = 1;
+    
+    /* Compile the file, loading it into the cache */
+    file_handle.type = ZEND_HANDLE_FILENAME;
+    file_handle.filename = filename;
+    file_handle.free_filename = 0;
+    file_handle.opened_path = NULL;
+    zend_try {
+        op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
+    } zend_catch {
+        apc_wprint("Error compiling %s in apc_compile_file.", filename);
+        op_array = NULL;
+    } zend_end_try();
+
+    /* Return class/function tables to previous states, destroy temp tables */
+    APCG(force_file_update) = 0;
+    CG(function_table) = cg_orig_function_table;
+    zend_hash_destroy(&cg_function_table);
+    CG(class_table) = cg_orig_class_table;
+    zend_hash_destroy(&cg_class_table);
+    EG(function_table) = eg_orig_function_table;
+    EG(class_table) = eg_orig_class_table;
+    
+    /* Restore global settings */
+    APCG(slam_defense) = slam_defense;
+    APCG(filters) = filters;
+    APCG(cache_by_default) = cache_by_default;
+
+    if(op_array == NULL) { RETURN_FALSE; }
+
+    /* Free up everything */
+    zend_destroy_file_handle(&file_handle TSRMLS_CC);
+#ifdef ZEND_ENGINE_2
+    destroy_op_array(op_array TSRMLS_CC);
+#else
+    destroy_op_array(op_array);
+#endif
+    efree(op_array);
+
+    RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ apc_functions[] */
+function_entry apc_functions[] = {
+       PHP_FE(apc_cache_info,          NULL)
+       PHP_FE(apc_clear_cache,         NULL)
+       PHP_FE(apc_sma_info,            NULL)
+       PHP_FE(apc_store,               NULL)
+       PHP_FE(apc_fetch,               NULL)
+       PHP_FE(apc_delete,              NULL)
+       PHP_FE(apc_define_constants,    NULL)
+       PHP_FE(apc_load_constants,      NULL)
+       PHP_FE(apc_compile_file,        NULL)
+       PHP_FE(apc_add,                 NULL)
+       {NULL,          NULL,                           NULL}
+};
+/* }}} */
+
+/* {{{ module definition structure */
+
+zend_module_entry apc_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "apc",
+       apc_functions,
+       PHP_MINIT(apc),
+       PHP_MSHUTDOWN(apc),
+       PHP_RINIT(apc),
+       PHP_RSHUTDOWN(apc),
+       PHP_MINFO(apc),
+       APC_VERSION,
+       STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_APC
+ZEND_GET_MODULE(apc)
+#endif
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: expandtab sw=4 ts=4 sts=4
+ */