--- /dev/null +++ b/ext/apc/apc_bin.c @@ -0,0 +1,987 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_bin.c 303383 2010-09-15 08:15:58Z dmitry $ */ + +/* Creates a binary architecture specific output to a string or file containing + * the current cache contents for both fies and user variables. This is accomplished + * via the apc_copy_* functions and "swizzling" pointer values to a position + * independent value, and unswizzling them on restoration. + */ + +#include "apc_globals.h" +#include "apc_bin.h" +#include "apc_zend.h" +#include "apc_sma.h" +#include "apc_pool.h" +#include "ext/standard/md5.h" +#include "ext/standard/crc32.h" + +extern apc_cache_t* apc_cache; +extern apc_cache_t* apc_user_cache; + +extern int _apc_store(char *strkey, int strkey_len, const zval *val, const uint ttl, const int exclusive TSRMLS_DC); /* this is hacky */ + +#define APC_BINDUMP_DEBUG 0 + + +#if APC_BINDUMP_DEBUG + +#define SWIZZLE(bd, ptr) \ + do { \ + if((long)bd < (long)ptr && (ulong)ptr < ((long)bd + bd->size)) { \ + printf("SWIZZLE: %x ~> ", ptr); \ + ptr = (void*)((long)(ptr) - (long)(bd)); \ + printf("%x in %s on line %d", ptr, __FILE__, __LINE__); \ + } else if((long)ptr > bd->size) { /* not swizzled */ \ + apc_error("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d" TSRMLS_CC, (long)bd, ptr, ((long)bd + bd->size), __FILE__, __LINE__); \ + } \ + printf("\n"); \ + } while(0); + +#define UNSWIZZLE(bd, ptr) \ + do { \ + printf("UNSWIZZLE: %x -> ", ptr); \ + ptr = (void*)((long)(ptr) + (long)(bd)); \ + printf("%x in %s on line %d \n", ptr, __FILE__, __LINE__); \ + } while(0); + +#else /* !APC_BINDUMP_DEBUG */ + +#define SWIZZLE(bd, ptr) \ + do { \ + if((long)bd < (long)ptr && (ulong)ptr < ((long)bd + bd->size)) { \ + ptr = (void*)((long)(ptr) - (long)(bd)); \ + } else if((ulong)ptr > bd->size) { /* not swizzled */ \ + apc_error("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d" TSRMLS_CC, (long)bd, ptr, ((long)bd + bd->size), __FILE__, __LINE__); \ + } \ + } while(0); + +#define UNSWIZZLE(bd, ptr) \ + do { \ + ptr = (void*)((long)(ptr) + (long)(bd)); \ + } while(0); + +#endif + + +static void *apc_bd_alloc(size_t size TSRMLS_DC); +static void apc_bd_free(void *ptr TSRMLS_DC); +static void *apc_bd_alloc_ex(void *ptr_new, size_t size TSRMLS_DC); + +typedef void (*apc_swizzle_cb_t)(apc_bd_t *bd, zend_llist *ll, void *ptr TSRMLS_DC); + +#if APC_BINDUMP_DEBUG +#define apc_swizzle_ptr(bd, ll, ptr) _apc_swizzle_ptr(bd, ll, (void*)ptr, __FILE__, __LINE__ TSRMLS_CC) +#else +#define apc_swizzle_ptr(bd, ll, ptr) _apc_swizzle_ptr(bd, ll, (void*)ptr, NULL, 0 TSRMLS_CC) +#endif + +static void _apc_swizzle_ptr(apc_bd_t *bd, zend_llist *ll, void **ptr, const char* file, int line TSRMLS_DC); +static void apc_swizzle_function(apc_bd_t *bd, zend_llist *ll, zend_function *func TSRMLS_DC); +static void apc_swizzle_class_entry(apc_bd_t *bd, zend_llist *ll, zend_class_entry *ce TSRMLS_DC); +static void apc_swizzle_hashtable(apc_bd_t *bd, zend_llist *ll, HashTable *ht, apc_swizzle_cb_t swizzle_cb, int is_ptr TSRMLS_DC); +static void apc_swizzle_zval(apc_bd_t *bd, zend_llist *ll, zval *zv TSRMLS_DC); +static void apc_swizzle_op_array(apc_bd_t *bd, zend_llist *ll, zend_op_array *op_array TSRMLS_DC); +static void apc_swizzle_property_info(apc_bd_t *bd, zend_llist *ll, zend_property_info *pi TSRMLS_DC); +static void apc_swizzle_function_entry(apc_bd_t *bd, zend_llist *ll, const zend_function_entry *fe TSRMLS_DC); +static void apc_swizzle_arg_info_array(apc_bd_t *bd, zend_llist *ll, const zend_arg_info* arg_info_array, uint num_args TSRMLS_DC); + +static apc_bd_t* apc_swizzle_bd(apc_bd_t* bd, zend_llist *ll TSRMLS_DC); +static int apc_unswizzle_bd(apc_bd_t *bd, int flags TSRMLS_DC); + + +/* {{{ apc_bd_alloc + * callback for copy_* functions */ +static void *apc_bd_alloc(size_t size TSRMLS_DC) { + return apc_bd_alloc_ex(NULL, size TSRMLS_CC); +} /* }}} */ + + +/* {{{ apc_bd_free + * callback for copy_* functions */ +static void apc_bd_free(void *ptr TSRMLS_DC) { + size_t *size; + if(zend_hash_index_find(&APCG(apc_bd_alloc_list), (ulong)ptr, (void**)&size) == FAILURE) { + apc_error("apc_bd_free could not free pointer (not found in list: %x)" TSRMLS_CC, ptr); + } + APCG(apc_bd_alloc_ptr) = (void*)((size_t)APCG(apc_bd_alloc_ptr) - *size); + zend_hash_index_del(&APCG(apc_bd_alloc_list), (ulong)ptr); +} /* }}} */ + + +/* {{{ apc_bd_alloc_ex + * set ranges or allocate a block of data from an already (e)malloc'd range. + * if ptr_new is not NULL, it will reset the pointer to start at ptr_new, + * with a range of size. If ptr_new is NULL, returns the next available + * block of given size. + */ +static void *apc_bd_alloc_ex(void *ptr_new, size_t size TSRMLS_DC) { + void *rval; + + rval = APCG(apc_bd_alloc_ptr); + if(ptr_new != NULL) { /* reset ptrs */ + APCG(apc_bd_alloc_ptr) = ptr_new; + APCG(apc_bd_alloc_ubptr) = (void*)((unsigned char *) ptr_new + size); + } else { /* alloc block */ + APCG(apc_bd_alloc_ptr) = (void*)((size_t)APCG(apc_bd_alloc_ptr) + size); +#if APC_BINDUMP_DEBUG + apc_notice("apc_bd_alloc: rval: 0x%x ptr: 0x%x ubptr: 0x%x size: %d" TSRMLS_CC, rval, APCG(apc_bd_alloc_ptr), APCG(apc_bd_alloc_ubptr), size); +#endif + if(APCG(apc_bd_alloc_ptr) > APCG(apc_bd_alloc_ubptr)) { + apc_error("Exceeded bounds check in apc_bd_alloc_ex by %d bytes." TSRMLS_CC, (unsigned char *) APCG(apc_bd_alloc_ptr) - (unsigned char *) APCG(apc_bd_alloc_ubptr)); + } + zend_hash_index_update(&APCG(apc_bd_alloc_list), (ulong)rval, &size, sizeof(size_t), NULL); + } + + return rval; +} /* }}} */ + + +/* {{{ _apc_swizzle_ptr */ +static void _apc_swizzle_ptr(apc_bd_t *bd, zend_llist *ll, void **ptr, const char* file, int line TSRMLS_DC) { + if(*ptr) { + if((long)bd < (long)*ptr && (ulong)*ptr < ((long)bd + bd->size)) { + zend_llist_add_element(ll, &ptr); +#if APC_BINDUMP_DEBUG + printf("[%06d] apc_swizzle_ptr: %x -> %x ", zend_llist_count(ll), ptr, *ptr); + printf(" in %s on line %d \n", file, line); +#endif + } else if((ulong)ptr > bd->size) { + apc_error("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d" TSRMLS_CC, (long)bd, *ptr, ((long)bd + bd->size), file, line); \ + } + } +} /* }}} */ + + +/* {{{ apc_swizzle_op_array */ +static void apc_swizzle_op_array(apc_bd_t *bd, zend_llist *ll, zend_op_array *op_array TSRMLS_DC) { + uint i; + +#ifdef ZEND_ENGINE_2 + apc_swizzle_arg_info_array(bd, ll, op_array->arg_info, op_array->num_args TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &op_array->arg_info); +#else + if (op_array->arg_types) { + apc_swizzle_ptr(bd, ll, &op_array->arg_types); + } +#endif + + apc_swizzle_ptr(bd, ll, &op_array->function_name); + apc_swizzle_ptr(bd, ll, &op_array->filename); + apc_swizzle_ptr(bd, ll, &op_array->refcount); + + /* swizzle op_array */ + for(i=0; i < op_array->last; i++) { +#ifndef ZEND_ENGINE_2_4 + if(op_array->opcodes[i].result.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].result.u.constant TSRMLS_CC); + } + if(op_array->opcodes[i].op1.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].op1.u.constant TSRMLS_CC); + } + if(op_array->opcodes[i].op2.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].op2.u.constant TSRMLS_CC); + } +#endif + switch (op_array->opcodes[i].opcode) { + case ZEND_JMP: +#ifdef ZEND_ENGINE_2_4 + apc_swizzle_ptr(bd, ll, &op_array->opcodes[i].op1.jmp_addr); +#else + apc_swizzle_ptr(bd, ll, &op_array->opcodes[i].op1.u.jmp_addr); +#endif + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: +#ifdef ZEND_ENGINE_2_4 + apc_swizzle_ptr(bd, ll, &op_array->opcodes[i].op2.jmp_addr); +#else + apc_swizzle_ptr(bd, ll, &op_array->opcodes[i].op2.u.jmp_addr); +#endif + } + } + apc_swizzle_ptr(bd, ll, &op_array->opcodes); + + /* break-continue array ptr */ + if(op_array->brk_cont_array) { + apc_swizzle_ptr(bd, ll, &op_array->brk_cont_array); + } + + /* static voriables */ + if(op_array->static_variables) { + apc_swizzle_hashtable(bd, ll, op_array->static_variables, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &op_array->static_variables); + } + +#ifdef ZEND_ENGINE_2 + /* try-catch */ + if(op_array->try_catch_array) { + apc_swizzle_ptr(bd, ll, &op_array->try_catch_array); + } +#endif + +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */ + /* vars */ + if(op_array->vars) { + for(i=0; (signed int) i < op_array->last_var; i++) { + apc_swizzle_ptr(bd, ll, &op_array->vars[i].name); + } + apc_swizzle_ptr(bd, ll, &op_array->vars); + } +#endif + +#ifdef ZEND_ENGINE_2 + /* doc comment */ + if(op_array->doc_comment) { + apc_swizzle_ptr(bd, ll, &op_array->doc_comment); + } +#endif + +} /* }}} */ + + +/* {{{ apc_swizzle_function */ +static void apc_swizzle_function(apc_bd_t *bd, zend_llist *ll, zend_function *func TSRMLS_DC) { + apc_swizzle_op_array(bd, ll, &func->op_array TSRMLS_CC); +#ifdef ZEND_ENGINE_2 + if(func->common.scope) { + apc_swizzle_ptr(bd, ll, &func->common.scope); + } +#endif +} /* }}} */ + + +/* {{{ apc_swizzle_class_entry */ +static void apc_swizzle_class_entry(apc_bd_t *bd, zend_llist *ll, zend_class_entry *ce TSRMLS_DC) { + + uint i; + + if(ce->name) { + apc_swizzle_ptr(bd, ll, &ce->name); + } + + if (ce->type == ZEND_USER_CLASS && ZEND_CE_DOC_COMMENT(ce)) { + apc_swizzle_ptr(bd, ll, &ZEND_CE_DOC_COMMENT(ce)); + } + +#ifndef ZEND_ENGINE_2 + apc_swizzle_ptr(bd, ll, &ce->refcount); +#endif + + apc_swizzle_hashtable(bd, ll, &ce->function_table, (apc_swizzle_cb_t)apc_swizzle_function, 0 TSRMLS_CC); +#ifdef ZEND_ENGINE_2_4 + if (ce->default_properties_table) { + int i; + + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->default_properties_table[i]) { + apc_swizzle_ptr(bd, ll, &ce->default_properties_table[i]); + apc_swizzle_zval(bd, ll, ce->default_properties_table[i] TSRMLS_CC); + } + } + } +#else + apc_swizzle_hashtable(bd, ll, &ce->default_properties, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); +#endif + +#ifdef ZEND_ENGINE_2 + apc_swizzle_hashtable(bd, ll, &ce->properties_info, (apc_swizzle_cb_t)apc_swizzle_property_info, 0 TSRMLS_CC); +#endif + +#ifdef ZEND_ENGINE_2_4 + if (ce->default_static_members_table) { + int i; + + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->default_static_members_table[i]) { + apc_swizzle_ptr(bd, ll, &ce->default_static_members_table[i]); + apc_swizzle_zval(bd, ll, ce->default_static_members_table[i] TSRMLS_CC); + } + } + } + ce->static_members_table = ce->default_static_members_table; +#else + apc_swizzle_hashtable(bd, ll, &ce->default_static_members, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + + if(ce->static_members != &ce->default_static_members) { + apc_swizzle_hashtable(bd, ll, ce->static_members, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + } else { + apc_swizzle_ptr(bd, ll, &ce->static_members); + } +#endif + + apc_swizzle_hashtable(bd, ll, &ce->constants_table, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + + if(ce->type == ZEND_INTERNAL_CLASS && ZEND_CE_BUILTIN_FUNCTIONS(ce)) { + for(i=0; ZEND_CE_BUILTIN_FUNCTIONS(ce)[i].fname; i++) { + apc_swizzle_function_entry(bd, ll, &ZEND_CE_BUILTIN_FUNCTIONS(ce)[i] TSRMLS_CC); + } + } + + apc_swizzle_ptr(bd, ll, &ce->constructor); + apc_swizzle_ptr(bd, ll, &ce->destructor); + apc_swizzle_ptr(bd, ll, &ce->clone); + apc_swizzle_ptr(bd, ll, &ce->__get); + apc_swizzle_ptr(bd, ll, &ce->__set); + apc_swizzle_ptr(bd, ll, &ce->__unset); + apc_swizzle_ptr(bd, ll, &ce->__isset); + apc_swizzle_ptr(bd, ll, &ce->__call); + apc_swizzle_ptr(bd, ll, &ce->serialize_func); + apc_swizzle_ptr(bd, ll, &ce->unserialize_func); + +#ifdef ZEND_ENGINE_2_2 + apc_swizzle_ptr(bd, ll, &ce->__tostring); +#endif + + if (ce->type == ZEND_USER_CLASS) { + apc_swizzle_ptr(bd, ll, &ZEND_CE_FILENAME(ce)); + } +} /* }}} */ + + +/* {{{ apc_swizzle_property_info */ +static void apc_swizzle_property_info(apc_bd_t *bd, zend_llist *ll, zend_property_info *pi TSRMLS_DC) { + apc_swizzle_ptr(bd, ll, &pi->name); + apc_swizzle_ptr(bd, ll, &pi->doc_comment); + +#ifdef ZEND_ENGINE_2_2 + apc_swizzle_ptr(bd, ll, &pi->ce); +#endif +} /* }}} */ + + +/* {{{ apc_swizzle_function_entry */ +static void apc_swizzle_function_entry(apc_bd_t *bd, zend_llist *ll, const zend_function_entry *fe TSRMLS_DC) { + apc_swizzle_ptr(bd, ll, &fe->fname); + apc_swizzle_arg_info_array(bd, ll, fe->arg_info, fe->num_args TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &fe->arg_info); +} /* }}} */ + + +/* {{{ apc_swizzle_arg_info_array */ +static void apc_swizzle_arg_info_array(apc_bd_t *bd, zend_llist *ll, const zend_arg_info* arg_info_array, uint num_args TSRMLS_DC) { + if(arg_info_array) { + uint i; + + for(i=0; i < num_args; i++) { + apc_swizzle_ptr(bd, ll, &arg_info_array[i].name); + apc_swizzle_ptr(bd, ll, &arg_info_array[i].class_name); + } + } + +} /* }}} */ + + +/* {{{ apc_swizzle_hashtable */ +static void apc_swizzle_hashtable(apc_bd_t *bd, zend_llist *ll, HashTable *ht, apc_swizzle_cb_t swizzle_cb, int is_ptr TSRMLS_DC) { + uint i; + Bucket **bp, **bp_prev; + + bp = &ht->pListHead; + while(*bp) { + bp_prev = bp; + bp = &(*bp)->pListNext; + if(is_ptr) { + swizzle_cb(bd, ll, *(void**)(*bp_prev)->pData TSRMLS_CC); + apc_swizzle_ptr(bd, ll, (*bp_prev)->pData); + } else { + swizzle_cb(bd, ll, (void**)(*bp_prev)->pData TSRMLS_CC); + } + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pData); + if((*bp_prev)->pDataPtr) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pDataPtr); + } + if((*bp_prev)->pListLast) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pListLast); + } + if((*bp_prev)->pNext) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pNext); + } + if((*bp_prev)->pLast) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pLast); + } + apc_swizzle_ptr(bd, ll, bp_prev); + } + for(i=0; i < ht->nTableSize; i++) { + if(ht->arBuckets[i]) { + apc_swizzle_ptr(bd, ll, &ht->arBuckets[i]); + } + } + apc_swizzle_ptr(bd, ll, &ht->pListTail); + + apc_swizzle_ptr(bd, ll, &ht->arBuckets); +} /* }}} */ + + +/* {{{ apc_swizzle_zval */ +static void apc_swizzle_zval(apc_bd_t *bd, zend_llist *ll, zval *zv TSRMLS_DC) { + + if(APCG(copied_zvals).nTableSize) { + if(zend_hash_index_exists(&APCG(copied_zvals), (ulong)zv)) { + return; + } + zend_hash_index_update(&APCG(copied_zvals), (ulong)zv, (void**)&zv, sizeof(zval*), NULL); + } + + switch(zv->type & ~IS_CONSTANT_INDEX) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_BOOL: + case IS_RESOURCE: + /* nothing to do */ + break; + case IS_CONSTANT: + case IS_STRING: + apc_swizzle_ptr(bd, ll, &zv->value.str.val); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + apc_swizzle_hashtable(bd, ll, zv->value.ht, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &zv->value.ht); + break; + case IS_OBJECT: + break; + default: + assert(0); /* shouldn't happen */ + } +} /* }}} */ + + +/* {{{ apc_swizzle_bd */ +static apc_bd_t* apc_swizzle_bd(apc_bd_t* bd, zend_llist *ll TSRMLS_DC) { + int count, i; + PHP_MD5_CTX context; + unsigned char digest[16]; + register php_uint32 crc; + php_uint32 crcinit = 0; + char *crc_p; + void ***ptr; + void ***ptr_list; + + count = zend_llist_count(ll); + ptr_list = emalloc(count * sizeof(void**)); + ptr = zend_llist_get_first(ll); + for(i=0; i < count; i++) { +#if APC_BINDUMP_DEBUG + printf("[%06d] ", i+1); +#endif + SWIZZLE(bd, **ptr); /* swizzle ptr */ + if((long)bd < (long)*ptr && (ulong)*ptr < ((long)bd + bd->size)) { /* exclude ptrs that aren't actually included in the ptr list */ +#if APC_BINDUMP_DEBUG + printf("[------] "); +#endif + SWIZZLE(bd, *ptr); /* swizzle ptr list */ + ptr_list[i] = *ptr; + } + ptr = zend_llist_get_next(ll); + } + SWIZZLE(bd, bd->entries); + + if(count > 0) { + bd = erealloc(bd, bd->size + (count * sizeof(void**))); + bd->num_swizzled_ptrs = count; + bd->swizzled_ptrs = (void***)((unsigned char *)bd + bd->size -2); /* extra -1 for null termination */ + bd->size += count * sizeof(void**); + memcpy(bd->swizzled_ptrs, ptr_list, count * sizeof(void**)); + SWIZZLE(bd, bd->swizzled_ptrs); + } else { + bd->num_swizzled_ptrs = 0; + bd->swizzled_ptrs = NULL; + } + ((char*)bd)[bd->size-1] = 0; /* silence null termination for zval strings */ + efree(ptr_list); + bd->swizzled = 1; + + /* Generate MD5/CRC32 checksum */ + for(i=0; i<16; i++) { bd->md5[i] = 0; } + bd->crc=0; + PHP_MD5Init(&context); + PHP_MD5Update(&context, (const unsigned char*)bd, bd->size); + PHP_MD5Final(digest, &context); + crc = crcinit^0xFFFFFFFF; + crc_p = (char*)bd; + for(i=bd->size; i--; ++crc_p) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*crc_p)) & 0xFF ]; + } + memcpy(bd->md5, digest, 16); + bd->crc = crc; + + return bd; +} /* }}} */ + + +/* {{{ apc_unswizzle_bd */ +static int apc_unswizzle_bd(apc_bd_t *bd, int flags TSRMLS_DC) { + int i; + unsigned char md5_orig[16]; + unsigned char digest[16]; + PHP_MD5_CTX context; + register php_uint32 crc; + php_uint32 crcinit = 0; + php_uint32 crc_orig; + char *crc_p; + + /* Verify the md5 or crc32 before we unswizzle */ + memcpy(md5_orig, bd->md5, 16); + for(i=0; i<16; i++) { bd->md5[i] = 0; } + crc_orig = bd->crc; + bd->crc=0; + if(flags & APC_BIN_VERIFY_MD5) { + PHP_MD5Init(&context); + PHP_MD5Update(&context, (const unsigned char*)bd, bd->size); + PHP_MD5Final(digest, &context); + if(memcmp(md5_orig, digest, 16)) { + apc_error("MD5 checksum of binary dump failed." TSRMLS_CC); + memcpy(bd->md5, md5_orig, 16); /* add back md5 checksum */ + return -1; + } + } + if(flags & APC_BIN_VERIFY_CRC32) { + crc = crcinit^0xFFFFFFFF; + crc_p = (char*)bd; + for(i=bd->size; i--; ++crc_p) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*crc_p)) & 0xFF ]; + } + if(crc_orig != crc) { + apc_error("CRC32 checksum of binary dump failed." TSRMLS_CC); + bd->crc = crc_orig; + return -1; + } + } + memcpy(bd->md5, md5_orig, 16); /* add back md5 checksum */ + bd->crc = crc_orig; + + UNSWIZZLE(bd, bd->entries); + UNSWIZZLE(bd, bd->swizzled_ptrs); + for(i=0; i < bd->num_swizzled_ptrs; i++) { + if(bd->swizzled_ptrs[i]) { + UNSWIZZLE(bd, bd->swizzled_ptrs[i]); + if(*bd->swizzled_ptrs[i] && (*bd->swizzled_ptrs[i] < (void*)bd)) { + UNSWIZZLE(bd, *bd->swizzled_ptrs[i]); + } + } + } + + bd->swizzled=0; + + return 0; +} /* }}} */ + + +/* {{{ apc_bin_checkfilter */ +static int apc_bin_checkfilter(HashTable *filter, const char *key, uint key_len) { + zval **zptr; + + if(filter == NULL) { + return 1; + } + + if(zend_hash_find(filter, (char*)key, key_len, (void**)&zptr) == SUCCESS) { + if(Z_TYPE_PP(zptr) == IS_LONG && Z_LVAL_PP(zptr) == 0) { + return 0; + } + } else { + return 0; + } + + + return 1; +} /* }}} */ + +/* {{{ apc_bin_fixup_op_array */ +static inline void apc_bin_fixup_op_array(zend_op_array *op_array) { + ulong i; + for (i = 0; i < op_array->last; i++) { + op_array->opcodes[i].handler = zend_opcode_handlers[APC_OPCODE_HANDLER_DECODE(&op_array->opcodes[i])]; + } +} +/* }}} */ + +/* {{{ apc_bin_fixup_class_entry */ +static inline void apc_bin_fixup_class_entry(zend_class_entry *ce) { + zend_function *fe; + HashPosition hpos; + + /* fixup the opcodes in each method */ + zend_hash_internal_pointer_reset_ex(&ce->function_table, &hpos); + while(zend_hash_get_current_data_ex(&ce->function_table, (void**)&fe, &hpos) == SUCCESS) { + apc_bin_fixup_op_array(&fe->op_array); + zend_hash_move_forward_ex(&ce->function_table, &hpos); + } + + /* fixup hashtable destructor pointers */ + ce->function_table.pDestructor = (dtor_func_t)zend_function_dtor; +#ifndef ZEND_ENGINE_2_4 + ce->default_properties.pDestructor = (dtor_func_t)zval_ptr_dtor_wrapper; +#endif + ce->properties_info.pDestructor = (dtor_func_t)zval_ptr_dtor_wrapper; +#ifndef ZEND_ENGINE_2_4 + ce->default_static_members.pDestructor = (dtor_func_t)zval_ptr_dtor_wrapper; + if (ce->static_members) { + ce->static_members->pDestructor = (dtor_func_t)zval_ptr_dtor_wrapper; + } +#endif + ce->constants_table.pDestructor = (dtor_func_t)zval_ptr_dtor_wrapper; +} +/* }}} */ + +/* {{{ apc_bin_dump */ +apc_bd_t* apc_bin_dump(HashTable *files, HashTable *user_vars TSRMLS_DC) { + + int i; + uint fcount; + slot_t *sp; + apc_bd_entry_t *ep; + int count=0; + apc_bd_t *bd; + zend_llist ll; + zend_function *efp, *sfp; + long size=0; + apc_context_t ctxt; + void *pool_ptr; + + zend_llist_init(&ll, sizeof(void*), NULL, 0); + zend_hash_init(&APCG(apc_bd_alloc_list), 0, NULL, NULL, 0); + + /* flip the hash for faster filter checking */ + files = apc_flip_hash(files); + user_vars = apc_flip_hash(user_vars); + + /* get size and entry counts */ + for(i=0; i < apc_user_cache->num_slots; i++) { + sp = apc_user_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(apc_bin_checkfilter(user_vars, sp->key.data.user.identifier, sp->key.data.user.identifier_len)) { + size += sizeof(apc_bd_entry_t*) + sizeof(apc_bd_entry_t); + size += sp->value->mem_size - (sizeof(apc_cache_entry_t) - sizeof(apc_cache_entry_value_t)); + count++; + } + } + } + for(i=0; i < apc_cache->num_slots; i++) { + sp = apc_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(sp->key.type == APC_CACHE_KEY_FPFILE) { + if(apc_bin_checkfilter(files, sp->key.data.fpfile.fullpath, sp->key.data.fpfile.fullpath_len+1)) { + size += sizeof(apc_bd_entry_t*) + sizeof(apc_bd_entry_t); + size += sp->value->mem_size - (sizeof(apc_cache_entry_t) - sizeof(apc_cache_entry_value_t)); + count++; + } + } else { + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load */ + apc_warning("Excluding some files from apc_bin_dump[file]. Cached files must be included using full path with apc.stat=0." TSRMLS_CC); + } + } + } + + size += sizeof(apc_bd_t) +1; /* +1 for null termination */ + bd = emalloc(size); + bd->size = size; + pool_ptr = emalloc(sizeof(apc_pool)); + apc_bd_alloc_ex(pool_ptr, sizeof(apc_pool) TSRMLS_CC); + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_bd_alloc, apc_bd_free, NULL, NULL TSRMLS_CC); /* ideally the pool wouldn't be alloc'd as part of this */ + if (!ctxt.pool) { /* TODO need to cleanup */ + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + return NULL; + } + ctxt.copy = APC_COPY_IN_USER; /* avoid stupid ALLOC_ZVAL calls here, hack */ + apc_bd_alloc_ex((void*)((long)bd + sizeof(apc_bd_t)), bd->size - sizeof(apc_bd_t) -1 TSRMLS_CC); + bd->num_entries = count; + bd->entries = apc_bd_alloc_ex(NULL, sizeof(apc_bd_entry_t) * count TSRMLS_CC); + + /* User entries */ + zend_hash_init(&APCG(copied_zvals), 0, NULL, NULL, 0); + count = 0; + for(i=0; i < apc_user_cache->num_slots; i++) { + sp = apc_user_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(apc_bin_checkfilter(user_vars, sp->key.data.user.identifier, sp->key.data.user.identifier_len)) { + ep = &bd->entries[count]; + ep->type = sp->value->type; + ep->val.user.info = apc_bd_alloc(sp->value->data.user.info_len TSRMLS_CC); + memcpy(ep->val.user.info, sp->value->data.user.info, sp->value->data.user.info_len); + ep->val.user.info_len = sp->value->data.user.info_len; + ep->val.user.val = apc_copy_zval(NULL, sp->value->data.user.val, &ctxt TSRMLS_CC); + ep->val.user.ttl = sp->value->data.user.ttl; + + /* swizzle pointers */ + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.user.info); + zend_hash_clean(&APCG(copied_zvals)); + apc_swizzle_zval(bd, &ll, bd->entries[count].val.user.val TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.user.val); + + count++; + } + } + } + zend_hash_destroy(&APCG(copied_zvals)); + APCG(copied_zvals).nTableSize=0; + + /* File entries */ + for(i=0; i < apc_cache->num_slots; i++) { + for(sp=apc_cache->slots[i]; sp != NULL; sp = sp->next) { + if(sp->key.type == APC_CACHE_KEY_FPFILE) { + if(apc_bin_checkfilter(files, sp->key.data.fpfile.fullpath, sp->key.data.fpfile.fullpath_len+1)) { + ep = &bd->entries[count]; + ep->type = sp->key.type; + ep->val.file.filename = apc_bd_alloc(strlen(sp->value->data.file.filename) + 1 TSRMLS_CC); + strcpy(ep->val.file.filename, sp->value->data.file.filename); + ep->val.file.op_array = apc_copy_op_array(NULL, sp->value->data.file.op_array, &ctxt TSRMLS_CC); + + for(ep->num_functions=0; sp->value->data.file.functions[ep->num_functions].function != NULL;) { ep->num_functions++; } + ep->val.file.functions = apc_bd_alloc(sizeof(apc_function_t) * ep->num_functions TSRMLS_CC); + for(fcount=0; fcount < ep->num_functions; fcount++) { + memcpy(&ep->val.file.functions[fcount], &sp->value->data.file.functions[fcount], sizeof(apc_function_t)); + ep->val.file.functions[fcount].name = apc_xmemcpy(sp->value->data.file.functions[fcount].name, sp->value->data.file.functions[fcount].name_len+1, apc_bd_alloc TSRMLS_CC); + ep->val.file.functions[fcount].name_len = sp->value->data.file.functions[fcount].name_len; + ep->val.file.functions[fcount].function = apc_bd_alloc(sizeof(zend_function) TSRMLS_CC); + efp = ep->val.file.functions[fcount].function; + sfp = sp->value->data.file.functions[fcount].function; + switch(sfp->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + efp->op_array = sfp->op_array; + break; + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + apc_copy_op_array(&efp->op_array, &sfp->op_array, &ctxt TSRMLS_CC); + break; + default: + assert(0); + } +#ifdef ZEND_ENGINE_2 + efp->common.prototype = NULL; + efp->common.fn_flags = sfp->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); +#endif + apc_swizzle_ptr(bd, &ll, &ep->val.file.functions[fcount].name); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.functions[fcount].function); + apc_swizzle_op_array(bd, &ll, &efp->op_array TSRMLS_CC); + } + + + for(ep->num_classes=0; sp->value->data.file.classes[ep->num_classes].class_entry != NULL;) { ep->num_classes++; } + ep->val.file.classes = apc_bd_alloc(sizeof(apc_class_t) * ep->num_classes TSRMLS_CC); + for(fcount=0; fcount < ep->num_classes; fcount++) { + ep->val.file.classes[fcount].name = apc_xmemcpy(sp->value->data.file.classes[fcount].name, sp->value->data.file.classes[fcount].name_len + 1, apc_bd_alloc TSRMLS_CC); + ep->val.file.classes[fcount].name_len = sp->value->data.file.classes[fcount].name_len; + ep->val.file.classes[fcount].class_entry = apc_copy_class_entry(NULL, sp->value->data.file.classes[fcount].class_entry, &ctxt TSRMLS_CC); + ep->val.file.classes[fcount].parent_name = apc_xstrdup(sp->value->data.file.classes[fcount].parent_name, apc_bd_alloc TSRMLS_CC); + + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].name); + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].parent_name); + apc_swizzle_class_entry(bd, &ll, ep->val.file.classes[fcount].class_entry TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].class_entry); + } + + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.file.filename); + apc_swizzle_op_array(bd, &ll, bd->entries[count].val.file.op_array TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.file.op_array); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.functions); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.classes); + + count++; + } else { + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load */ + } + } + } + } + + /* append swizzle pointer list to bd */ + bd = apc_swizzle_bd(bd, &ll TSRMLS_CC); + zend_llist_destroy(&ll); + zend_hash_destroy(&APCG(apc_bd_alloc_list)); + + if(files) { + zend_hash_destroy(files); + efree(files); + } + if(user_vars) { + zend_hash_destroy(user_vars); + efree(user_vars); + } + + efree(pool_ptr); + + return bd; +} /* }}} */ + + +/* {{{ apc_bin_load */ +int apc_bin_load(apc_bd_t *bd, int flags TSRMLS_DC) { + + apc_bd_entry_t *ep; + uint i, i2; + int ret; + time_t t; + zend_op_array *alloc_op_array = NULL; + apc_function_t *alloc_functions = NULL; + apc_class_t *alloc_classes = NULL; + apc_cache_entry_t *cache_entry; + apc_cache_key_t cache_key; + apc_context_t ctxt; + + if (bd->swizzled) { + if(apc_unswizzle_bd(bd, flags TSRMLS_CC) < 0) { + return -1; + } + } + + t = apc_time(); + + for(i = 0; i < bd->num_entries; i++) { + ctxt.pool = apc_pool_create(APC_SMALL_POOL, apc_sma_malloc, apc_sma_free, apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + if (!ctxt.pool) { /* TODO need to cleanup previous pools */ + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + goto failure; + } + ep = &bd->entries[i]; + switch (ep->type) { + case APC_CACHE_KEY_FILE: + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load (or something else perhaps?) */ + break; + case APC_CACHE_KEY_FPFILE: + ctxt.copy = APC_COPY_IN_OPCODE; + + HANDLE_BLOCK_INTERRUPTIONS(); +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + if(!apc_cache_write_lock(apc_cache TSRMLS_CC)) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + return -1; + } + } +#endif + if(! (alloc_op_array = apc_copy_op_array(NULL, ep->val.file.op_array, &ctxt TSRMLS_CC))) { + goto failure; + } + apc_bin_fixup_op_array(alloc_op_array); + + if(! (alloc_functions = apc_sma_malloc(sizeof(apc_function_t) * (ep->num_functions + 1) TSRMLS_CC))) { + goto failure; + } + for(i2=0; i2 < ep->num_functions; i2++) { + if(! (alloc_functions[i2].name = apc_xmemcpy(ep->val.file.functions[i2].name, ep->val.file.functions[i2].name_len + 1, apc_sma_malloc TSRMLS_CC))) { + goto failure; + } + alloc_functions[i2].name_len = ep->val.file.functions[i2].name_len; + if(! (alloc_functions[i2].function = apc_sma_malloc(sizeof(zend_function) TSRMLS_CC))) { + goto failure; + } + switch(ep->val.file.functions[i2].function->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + alloc_functions[i2].function->op_array = ep->val.file.functions[i2].function->op_array; + break; + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + if (!apc_copy_op_array(&alloc_functions[i2].function->op_array, &ep->val.file.functions[i2].function->op_array, &ctxt TSRMLS_CC)) { + goto failure; + } + apc_bin_fixup_op_array(&alloc_functions[i2].function->op_array); + break; + default: + assert(0); + } +#ifdef ZEND_ENGINE_2 + alloc_functions[i2].function->common.prototype=NULL; + alloc_functions[i2].function->common.fn_flags=ep->val.file.functions[i2].function->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); +#endif + } + alloc_functions[i2].name = NULL; + alloc_functions[i2].function = NULL; + + if(! (alloc_classes = apc_sma_malloc(sizeof(apc_class_t) * (ep->num_classes + 1) TSRMLS_CC))) { + goto failure; + } + for(i2=0; i2 < ep->num_classes; i2++) { + if(! (alloc_classes[i2].name = apc_xmemcpy(ep->val.file.classes[i2].name, ep->val.file.classes[i2].name_len+1, apc_sma_malloc TSRMLS_CC))) { + goto failure; + } + alloc_classes[i2].name_len = ep->val.file.classes[i2].name_len; + if(! (alloc_classes[i2].class_entry = apc_copy_class_entry(NULL, ep->val.file.classes[i2].class_entry, &ctxt TSRMLS_CC))) { + goto failure; + } + apc_bin_fixup_class_entry(alloc_classes[i2].class_entry); + if(! (alloc_classes[i2].parent_name = apc_xstrdup(ep->val.file.classes[i2].parent_name, apc_sma_malloc TSRMLS_CC))) { + if(ep->val.file.classes[i2].parent_name != NULL) { + goto failure; + } + } + } + alloc_classes[i2].name = NULL; + alloc_classes[i2].class_entry = NULL; + + if(!(cache_entry = apc_cache_make_file_entry(ep->val.file.filename, alloc_op_array, alloc_functions, alloc_classes, &ctxt TSRMLS_CC))) { + goto failure; + } + + if (!apc_cache_make_file_key(&cache_key, ep->val.file.filename, PG(include_path), t TSRMLS_CC)) { + goto failure; + } + + if ((ret = apc_cache_insert(apc_cache, cache_key, cache_entry, &ctxt, t TSRMLS_CC)) != 1) { + if(ret==-1) { + goto failure; + } + } + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache TSRMLS_CC); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + + break; + case APC_CACHE_KEY_USER: + ctxt.copy = APC_COPY_IN_USER; + _apc_store(ep->val.user.info, ep->val.user.info_len, ep->val.user.val, ep->val.user.ttl, 0 TSRMLS_CC); + break; + default: + break; + } + } + + return 0; + +failure: + apc_pool_destroy(ctxt.pool TSRMLS_CC); + apc_warning("Unable to allocate memory for apc binary load/dump functionality." TSRMLS_CC); +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache TSRMLS_CC); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return -1; +} /* }}} */ + + +/* + * 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 + */ --- /dev/null +++ b/ext/apc/apc_bin.h @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_bin.h 300979 2010-07-04 10:15:05Z kalle $ */ + +#ifndef APC_BINDUMP_H +#define APC_BINDUMP_H + +#include "apc.h" +#include "apc_php.h" +#include "ext/standard/basic_functions.h" + +/* APC binload flags */ +#define APC_BIN_VERIFY_MD5 1 << 0 +#define APC_BIN_VERIFY_CRC32 1 << 1 + +typedef struct _apc_bd_entry_t { + unsigned char type; + uint num_functions; + uint num_classes; + apc_cache_entry_value_t val; +} apc_bd_entry_t; + +typedef struct _apc_bd_t { + unsigned int size; + int swizzled; + unsigned char md5[16]; + php_uint32 crc; + unsigned int num_entries; + apc_bd_entry_t *entries; + int num_swizzled_ptrs; + void ***swizzled_ptrs; +} apc_bd_t; + +apc_bd_t* apc_bin_dump(HashTable *files, HashTable *user_vars TSRMLS_DC); +int apc_bin_load(apc_bd_t *bd, int flags TSRMLS_DC); + +#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 + */ --- /dev/null +++ b/ext/apc/apc.c @@ -0,0 +1,636 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc.c 305003 2010-10-31 19:45:10Z gopalv $ */ + +#include "apc.h" +#include "apc_zend.h" +#include "apc_cache.h" +#include "php.h" + +#if HAVE_PCRE || HAVE_BUNDLED_PCRE +/* Deal with problem present until php-5.2.2 where php_pcre.h was not installed correctly */ +# if !HAVE_BUNDLED_PCRE && PHP_MAJOR_VERSION == 5 && (PHP_MINOR_VERSION < 2 || (PHP_MINOR_VERSION == 2 && PHP_RELEASE_VERSION < 2)) +# include "apc_php_pcre.h" +# else +# include "ext/pcre/php_pcre.h" +# endif +# include "ext/standard/php_smart_str.h" +#endif + +#define NELEMS(a) (sizeof(a)/sizeof((a)[0])) + +/* {{{ memory allocation wrappers */ + +void* apc_emalloc(size_t n TSRMLS_DC) +{ + void* p = malloc(n); + if (p == NULL) { + apc_error("apc_emalloc: malloc failed to allocate %u bytes:" TSRMLS_CC, n); + } + return p; +} + +void* apc_erealloc(void* p, size_t n TSRMLS_DC) +{ + p = realloc(p, n); + if (p == NULL) { + apc_error("apc_erealloc: realloc failed to allocate %u bytes:" TSRMLS_CC, n); + } + return p; +} + +void apc_efree(void* p TSRMLS_DC) +{ + if (p == NULL) { + apc_error("apc_efree: attempt to free null pointer" TSRMLS_CC); + } + free(p); +} + +char* apc_estrdup(const char* s TSRMLS_DC) +{ + int len; + char* dup; + + if (s == NULL) { + return NULL; + } + len = strlen(s); + dup = (char*) malloc(len+1); + if (dup == NULL) { + apc_error("apc_estrdup: malloc failed to allocate %u bytes:" TSRMLS_CC, len+1); + } + memcpy(dup, s, len); + dup[len] = '\0'; + return dup; +} + +void* apc_xstrdup(const char* s, apc_malloc_t f TSRMLS_DC) +{ + return s != NULL ? apc_xmemcpy(s, strlen(s)+1, f TSRMLS_CC) : NULL; +} + +void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f TSRMLS_DC) +{ + void* q; + + if (p != NULL && (q = f(n TSRMLS_CC)) != NULL) { + memcpy(q, p, n); + return q; + } + return NULL; +} + +/* }}} */ + +/* {{{ console display functions */ +#ifdef ZTS +# define APC_PRINT_FUNCTION_PARAMETER TSRMLS_C +#else +# define APC_PRINT_FUNCTION_PARAMETER format +#endif + +#define APC_PRINT_FUNCTION(name, verbosity) \ + void apc_##name(const char *format TSRMLS_DC, ...) \ + { \ + va_list args; \ + \ + va_start(args, APC_PRINT_FUNCTION_PARAMETER); \ + php_verror(NULL, "", verbosity, format, args TSRMLS_CC); \ + va_end(args); \ + } + +APC_PRINT_FUNCTION(error, E_ERROR) +APC_PRINT_FUNCTION(warning, E_WARNING) +APC_PRINT_FUNCTION(notice, E_NOTICE) + +#ifdef __DEBUG_APC__ +APC_PRINT_FUNCTION(debug, E_NOTICE) +#else +void apc_debug(const char *format TSRMLS_DC, ...) {} +#endif +/* }}} */ + +/* {{{ string and text manipulation */ + +char* apc_append(const char* s, const char* t TSRMLS_DC) +{ + int slen; + int tlen; + char* p; + + slen = strlen(s); + tlen = strlen(t); + + p = (char*) apc_emalloc((slen + tlen + 1) * sizeof(char) TSRMLS_CC); + memcpy(p, s, slen); + memcpy(p + slen, t, tlen + 1); + + return p; +} + +char* apc_substr(const char* s, int start, int length TSRMLS_DC) +{ + char* substr; + int src_len = strlen(s); + + /* bring start into range */ + if (start < 0) { + start = 0; + } + else if (start >= src_len) { + start = src_len - 1; + } + + /* bring length into range */ + if (length < 0 || src_len - start < length) { + length = src_len - start; + } + + /* create the substring */ + substr = apc_xmemcpy(s + start, length + 1, apc_emalloc TSRMLS_CC); + substr[length] = '\0'; + return substr; +} + +char** apc_tokenize(const char* s, char delim TSRMLS_DC) +{ + char** tokens; /* array of tokens, NULL terminated */ + int size; /* size of tokens array */ + int n; /* index of next token in tokens array */ + int cur; /* current position in input string */ + int end; /* final legal position in input string */ + int next; /* position of next delimiter in input */ + + if (!s) { + return NULL; + } + + size = 2; + n = 0; + cur = 0; + end = strlen(s) - 1; + + tokens = (char**) apc_emalloc(size * sizeof(char*) TSRMLS_CC); + tokens[n] = NULL; + + while (cur <= end) { + /* search for the next delimiter */ + char* p = strchr(s + cur, delim); + next = p ? p-s : end+1; + + /* resize token array if necessary */ + if (n == size-1) { + size *= 2; + tokens = (char**) apc_erealloc(tokens, size * sizeof(char*) TSRMLS_CC); + } + + /* save the current token */ + tokens[n] = apc_substr(s, cur, next-cur TSRMLS_CC); + + tokens[++n] = NULL; + cur = next + 1; + } + + return tokens; +} + +/* }}} */ + + +/* {{{ apc_win32_restat */ +#ifdef PHP_WIN32 +static int apc_restat(apc_fileinfo_t *fileinfo TSRMLS_DC) +{ + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION hInfo; + + hFile = CreateFile(fileinfo->fullpath, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (!hFile) { + apc_debug("Cannot create a file HANDLE for %s\n" TSRMLS_CC, fileinfo->fullpath); + return -1; + } + + if (!GetFileInformationByHandle(hFile, &hInfo)) { + apc_debug("Cannot get file information from handle\n" TSRMLS_CC); + CloseHandle(hFile); + return -1; + } + + CloseHandle(hFile); + + fileinfo->st_buf.sb.st_dev = hInfo.dwVolumeSerialNumber; + fileinfo->st_buf.sb.st_ino = (((apc_ino_t)(hInfo.nFileIndexHigh) << 32) | (apc_ino_t) hInfo.nFileIndexLow); + + return 0; +} +#else +static int apc_restat(apc_fileinfo_t *fileinfo TSRMLS_DC) +{ + return 0; +} +#endif +/* }}} */ + +/* {{{ apc_search_paths */ +/* similar to php_stream_stat_path */ +#define APC_URL_STAT(wrapper, filename, pstatbuf) \ + ((wrapper)->wops->url_stat((wrapper), (filename), PHP_STREAM_URL_STAT_QUIET, (pstatbuf), NULL TSRMLS_CC)) + +/* copy out to path_buf if path_for_open isn't the same as filename */ +#define COPY_IF_CHANGED(p) \ + (char*) (((p) == filename) ? filename : \ + (strlcpy((char*)fileinfo->path_buf, (p), sizeof(fileinfo->path_buf))) \ + ? (fileinfo->path_buf) : NULL) + +/* len checks can be skipped here because filename is NUL terminated */ +#define IS_RELATIVE_PATH(filename, len) \ + ((filename) && (filename[0] == '.' && \ + (IS_SLASH(filename[1]) || \ + (filename[1] == '.' && \ + IS_SLASH(filename[2]))))) + + +int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo TSRMLS_DC) +{ + char** paths = NULL; + char *exec_fname; + int exec_fname_length; + int found = 0; + int i; + php_stream_wrapper *wrapper = NULL; + char *path_for_open = NULL; + + assert(filename && fileinfo); + + + wrapper = php_stream_locate_url_wrapper(filename, &path_for_open, 0 TSRMLS_CC); + + if(!wrapper || !wrapper->wops || !wrapper->wops->url_stat) { + return -1; + } + + if(wrapper != &php_plain_files_wrapper) { + if(APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) { + fileinfo->fullpath = COPY_IF_CHANGED(path_for_open); + return apc_restat(fileinfo TSRMLS_CC); + } + return -1; /* cannot stat */ + } + + if (IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) && + APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) { + fileinfo->fullpath = COPY_IF_CHANGED(path_for_open); + return apc_restat(fileinfo TSRMLS_CC); + } + + if (!IS_RELATIVE_PATH(path_for_open, strlen(path_for_open))) { + paths = apc_tokenize(path, DEFAULT_DIR_SEPARATOR TSRMLS_CC); + if (!paths) + return -1; + + /* for each directory in paths, look for filename inside */ + for (i = 0; paths[i]; i++) { + snprintf(fileinfo->path_buf, sizeof(fileinfo->path_buf), "%s%c%s", paths[i], DEFAULT_SLASH, path_for_open); + if (APC_URL_STAT(wrapper, fileinfo->path_buf, &fileinfo->st_buf) == 0) { + fileinfo->fullpath = (char*) fileinfo->path_buf; + found = 1; + break; + } + } + } + + /* check in path of the calling scripts' current working directory */ + /* modified from main/streams/plain_wrapper.c */ + if(!found && zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + while((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if((exec_fname && exec_fname[0] != '[') && exec_fname_length > 0) { + /* not: [no active file] or no path */ + memcpy(fileinfo->path_buf, exec_fname, exec_fname_length); + fileinfo->path_buf[exec_fname_length] = DEFAULT_SLASH; + strlcpy(fileinfo->path_buf +exec_fname_length +1, path_for_open,sizeof(fileinfo->path_buf)-exec_fname_length-1); + /* apc_warning("filename: %s, exec_fname: %s, fileinfo->path_buf: %s" TSRMLS_CC, path_for_open, exec_fname, fileinfo->path_buf); */ + if (APC_URL_STAT(wrapper, fileinfo->path_buf, &fileinfo->st_buf) == 0) { + fileinfo->fullpath = (char*) fileinfo->path_buf; + found = 1; + } + } + } + + if(paths) { + /* free the value returned by apc_tokenize */ + for (i = 0; paths[i]; i++) { + apc_efree(paths[i] TSRMLS_CC); + } + apc_efree(paths TSRMLS_CC); + } + + return found ? apc_restat(fileinfo TSRMLS_CC) : -1; +} + +/* }}} */ + +/* {{{ regular expression wrapper functions */ + +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) +typedef struct { + pcre *preg; + pcre *nreg; +} apc_regex; + +#define APC_ADD_PATTERN(match, pat) do {\ + if(match.len > 1) {\ + smart_str_appendc(&match, '|');\ + }\ + smart_str_appendc(&match, '(');\ + while(*pat) {\ + if(*pat == '/') smart_str_appendc(&match, '\\');\ + \ + smart_str_appendc(&match, *(pat++));\ + }\ + smart_str_appendc(&match, ')');\ +} while(0) + +#define APC_COMPILE_PATTERN(re, match) do {\ + if(match.len > 2) { /* more than just "//" */\ + if (((re) = pcre_get_compiled_regex(match.c, NULL, NULL TSRMLS_CC)) == NULL) {\ + apc_warning("apc_regex_compile_array: invalid expression '%s'" TSRMLS_CC, match.c); \ + smart_str_free(&match);\ + return NULL;\ + }\ + } else { \ + (re) = NULL;\ + }\ +} while(0) + +void* apc_regex_compile_array(char* patterns[] TSRMLS_DC) +{ + apc_regex* regs; + int npat; + smart_str pmatch = {0,}; + smart_str nmatch = {0,}; + char* pattern; + + if (!patterns) + return NULL; + + regs = (apc_regex*) apc_emalloc(sizeof(apc_regex) TSRMLS_CC); + + smart_str_appendc(&pmatch, '/'); + smart_str_appendc(&nmatch, '/'); + + for (npat = 0; patterns[npat] != NULL; npat++) { + pattern = patterns[npat]; + if(pattern[0] == '+') { + pattern += sizeof(char); + APC_ADD_PATTERN(pmatch, pattern); + } else { + if(pattern[0] == '-') pattern += sizeof(char); + APC_ADD_PATTERN(nmatch, pattern); + } + } + smart_str_appendc(&pmatch, '/'); + smart_str_appendc(&nmatch, '/'); + + smart_str_0(&nmatch); + smart_str_0(&pmatch); + + APC_COMPILE_PATTERN(regs->preg, pmatch); + APC_COMPILE_PATTERN(regs->nreg, nmatch); + + smart_str_free(&pmatch); + smart_str_free(&nmatch); + + return (void*) regs; +} + +void apc_regex_destroy_array(void* p TSRMLS_DC) +{ + if (p != NULL) { + apc_regex* regs = (apc_regex*) p; + apc_efree(regs TSRMLS_CC); + } +} + +#define APC_MATCH_PATTERN(re, input, output) do {\ + if (re && pcre_exec(re, NULL, (input), strlen(input), 0, 0, NULL, 0) >= 0) {\ + return (output);\ + }\ +} while(0) + + +int apc_regex_match_array(void* p, const char* input) +{ + apc_regex* regs; + + if (!p) + return 0; + + regs = (apc_regex*) p; + + APC_MATCH_PATTERN(regs->preg, input, APC_POSITIVE_MATCH); + APC_MATCH_PATTERN(regs->nreg, input, APC_NEGATIVE_MATCH); + + return 0; +} +#else /* no pcre */ +void* apc_regex_compile_array(char* patterns[] TSRMLS_DC) +{ + if(patterns && patterns[0] != NULL) { + apc_warning("pcre missing, disabling filters" TSRMLS_CC); + } + return NULL; +} +void apc_regex_destroy_array(void* p) +{ + /* nothing */ +} +int apc_regex_match_array(void* p, const char* input) +{ + return 0; +} +#endif +/* }}} */ + +/* {{{ crc32 implementation */ + +/* this table was generated by crc32gen() */ +static unsigned int crc32tab[] = { + /* 0 */ 0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd, + /* 4 */ 0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1, + /* 8 */ 0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4, + /* 12 */ 0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8, + /* 16 */ 0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef, + /* 20 */ 0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3, + /* 24 */ 0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6, + /* 28 */ 0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da, + /* 32 */ 0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9, + /* 36 */ 0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995, + /* 40 */ 0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0, + /* 44 */ 0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c, + /* 48 */ 0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b, + /* 52 */ 0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7, + /* 56 */ 0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492, + /* 60 */ 0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be, + /* 64 */ 0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815, + /* 68 */ 0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939, + /* 72 */ 0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c, + /* 76 */ 0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20, + /* 80 */ 0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027, + /* 84 */ 0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b, + /* 88 */ 0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e, + /* 92 */ 0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512, + /* 96 */ 0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871, + /* 100 */ 0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d, + /* 104 */ 0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68, + /* 108 */ 0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44, + /* 112 */ 0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043, + /* 116 */ 0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f, + /* 120 */ 0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a, + /* 124 */ 0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576, + /* 128 */ 0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d, + /* 132 */ 0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861, + /* 136 */ 0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54, + /* 140 */ 0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78, + /* 144 */ 0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f, + /* 148 */ 0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053, + /* 152 */ 0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566, + /* 156 */ 0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a, + /* 160 */ 0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929, + /* 164 */ 0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805, + /* 168 */ 0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30, + /* 172 */ 0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c, + /* 176 */ 0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b, + /* 180 */ 0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037, + /* 184 */ 0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502, + /* 188 */ 0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e, + /* 192 */ 0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985, + /* 196 */ 0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9, + /* 200 */ 0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c, + /* 204 */ 0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0, + /* 208 */ 0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7, + /* 212 */ 0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b, + /* 216 */ 0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae, + /* 220 */ 0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482, + /* 224 */ 0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1, + /* 228 */ 0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd, + /* 232 */ 0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8, + /* 236 */ 0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4, + /* 240 */ 0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3, + /* 244 */ 0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff, + /* 248 */ 0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca, + /* 252 */ 0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6, +}; + +unsigned int apc_crc32(const char* buf, int len) +{ + int i; + int k; + unsigned int crc; + + /* preconditioning */ + crc = 0xFFFFFFFF; + + for (i = 0; i < len; i++) { + k = (crc ^ buf[i]) & 0x000000FF; + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[k]; + } + + /* postconditioning */ + return ~crc; +} + +/* crc32gen: generate the nth (0..255) crc32 table value */ +#if 0 +static unsigned long crc32gen(int n) +{ + int i; + unsigned long crc; + + crc = n; + for (i = 8; i >= 0; i--) { + if (crc & 1) { + crc = (crc >> 1) ^ 0xEDB88320; + } + else { + crc >>= 1; + } + } + return crc; +} +#endif + +/* }}} */ + + +/* {{{ apc_flip_hash() */ +HashTable* apc_flip_hash(HashTable *hash) { + zval **entry, *data; + HashTable *new_hash; + HashPosition pos; + + if(hash == NULL) return hash; + + MAKE_STD_ZVAL(data); + ZVAL_LONG(data, 1); + + new_hash = emalloc(sizeof(HashTable)); + zend_hash_init(new_hash, hash->nTableSize, NULL, ZVAL_PTR_DTOR, 0); + + zend_hash_internal_pointer_reset_ex(hash, &pos); + while (zend_hash_get_current_data_ex(hash, (void **)&entry, &pos) == SUCCESS) { + if(Z_TYPE_PP(entry) == IS_STRING) { + zend_hash_update(new_hash, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) +1, &data, sizeof(data), NULL); + } else { + zend_hash_index_update(new_hash, Z_LVAL_PP(entry), &data, sizeof(data), NULL); + } + Z_ADDREF_P(data); + zend_hash_move_forward_ex(hash, &pos); + } + zval_ptr_dtor(&data); + + return new_hash; +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_cache.c @@ -0,0 +1,1295 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_cache.c 305771 2010-11-26 12:57:16Z gopalv $ */ + +#include "apc_cache.h" +#include "apc_zend.h" +#include "apc_sma.h" +#include "apc_globals.h" +#include "SAPI.h" + +/* TODO: rehash when load factor exceeds threshold */ + +#define CHECK(p) { if ((p) == NULL) return NULL; } + +/* {{{ key_equals */ +#define key_equals(a, b) (a.inode==b.inode && a.device==b.device) +/* }}} */ + +static void apc_cache_expunge(apc_cache_t* cache, size_t size TSRMLS_DC); + +/* {{{ hash */ +static unsigned int hash(apc_cache_key_t key) +{ + return (unsigned int)(key.data.file.device + key.data.file.inode); +} +/* }}} */ + +/* {{{ string_nhash_8 */ +#define string_nhash_8(s,len) (unsigned int)(zend_inline_hash_func(s, len)) +/* }}} */ + +/* {{{ make_prime */ +static int const primes[] = { + 257, /* 256 */ + 521, /* 512 */ + 1031, /* 1024 */ + 2053, /* 2048 */ + 3079, /* 3072 */ + 4099, /* 4096 */ + 5147, /* 5120 */ + 6151, /* 6144 */ + 7177, /* 7168 */ + 8209, /* 8192 */ + 9221, /* 9216 */ +10243, /* 10240 */ +#if 0 +11273, /* 11264 */ +12289, /* 12288 */ +13313, /* 13312 */ +14341, /* 14336 */ +15361, /* 15360 */ +16411, /* 16384 */ +17417, /* 17408 */ +18433, /* 18432 */ +19457, /* 19456 */ +#endif +0 /* sentinel */ +}; + +static int make_prime(int n) +{ + int *k = (int*)primes; + while(*k) { + if((*k) > n) return *k; + k++; + } + return *(k-1); +} +/* }}} */ + +/* {{{ make_slot */ +slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t TSRMLS_DC) +{ + slot_t* p = apc_pool_alloc(value->pool, sizeof(slot_t)); + + if (!p) return NULL; + + if(value->type == APC_CACHE_ENTRY_USER) { + char *identifier = (char*) apc_pmemcpy(key.data.user.identifier, key.data.user.identifier_len, value->pool TSRMLS_CC); + if (!identifier) { + return NULL; + } + key.data.user.identifier = identifier; + } else if(key.type == APC_CACHE_KEY_FPFILE) { + char *fullpath = (char*) apc_pstrdup(key.data.fpfile.fullpath, value->pool TSRMLS_CC); + if (!fullpath) { + return NULL; + } + key.data.fpfile.fullpath = fullpath; + } + p->key = key; + p->value = value; + p->next = next; + p->num_hits = 0; + p->creation_time = t; + p->access_time = t; + p->deletion_time = 0; + return p; +} +/* }}} */ + +/* {{{ free_slot */ +static void free_slot(slot_t* slot TSRMLS_DC) +{ + apc_pool_destroy(slot->value->pool TSRMLS_CC); +} +/* }}} */ + +/* {{{ remove_slot */ +static void remove_slot(apc_cache_t* cache, slot_t** slot TSRMLS_DC) +{ + slot_t* dead = *slot; + *slot = (*slot)->next; + + cache->header->mem_size -= dead->value->mem_size; + cache->header->num_entries--; + if (dead->value->ref_count <= 0) { + free_slot(dead TSRMLS_CC); + } + else { + dead->next = cache->header->deleted_list; + dead->deletion_time = time(0); + cache->header->deleted_list = dead; + } +} +/* }}} */ + +/* {{{ process_pending_removals */ +static void process_pending_removals(apc_cache_t* cache TSRMLS_DC) +{ + slot_t** slot; + time_t now; + + /* This function scans the list of removed cache entries and deletes any + * entry whose reference count is zero (indicating that it is no longer + * being executed) or that has been on the pending list for more than + * cache->gc_ttl seconds (we issue a warning in the latter case). + */ + + if (!cache->header->deleted_list) + return; + + slot = &cache->header->deleted_list; + now = time(0); + + while (*slot != NULL) { + int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0; + + if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) { + slot_t* dead = *slot; + + if (dead->value->ref_count > 0) { + switch(dead->value->type) { + case APC_CACHE_ENTRY_FILE: + apc_warning("GC cache entry '%s' (dev=%d ino=%d) was on gc-list for %d seconds" TSRMLS_CC, + dead->value->data.file.filename, dead->key.data.file.device, dead->key.data.file.inode, gc_sec); + break; + case APC_CACHE_ENTRY_USER: + apc_warning("GC cache entry '%s'was on gc-list for %d seconds" TSRMLS_CC, dead->value->data.user.info, gc_sec); + break; + } + } + *slot = dead->next; + free_slot(dead TSRMLS_CC); + } + else { + slot = &(*slot)->next; + } + } +} +/* }}} */ + +/* {{{ prevent_garbage_collection */ +static void prevent_garbage_collection(apc_cache_entry_t* entry) +{ + /* set reference counts on zend objects to an arbitrarily high value to + * prevent garbage collection after execution */ + + enum { BIG_VALUE = 1000 }; + + if(entry->data.file.op_array) { + entry->data.file.op_array->refcount[0] = BIG_VALUE; + } + if (entry->data.file.functions) { + int i; + apc_function_t* fns = entry->data.file.functions; + for (i=0; fns[i].function != NULL; i++) { + *(fns[i].function->op_array.refcount) = BIG_VALUE; + } + } + if (entry->data.file.classes) { + int i; + apc_class_t* classes = entry->data.file.classes; + for (i=0; classes[i].class_entry != NULL; i++) { + classes[i].class_entry->refcount = BIG_VALUE; + } + } +} +/* }}} */ + +/* {{{ apc_cache_create */ +apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl TSRMLS_DC) +{ + apc_cache_t* cache; + int cache_size; + int num_slots; + + num_slots = make_prime(size_hint > 0 ? size_hint : 2000); + + cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t) TSRMLS_CC); + cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*); + + cache->shmaddr = apc_sma_malloc(cache_size TSRMLS_CC); + if(!cache->shmaddr) { + apc_error("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). " TSRMLS_CC); + } + memset(cache->shmaddr, 0, cache_size); + + cache->header = (cache_header_t*) cache->shmaddr; + cache->header->num_hits = 0; + cache->header->num_misses = 0; + cache->header->deleted_list = NULL; + cache->header->start_time = time(NULL); + cache->header->expunges = 0; + cache->header->busy = 0; + + cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t)); + cache->num_slots = num_slots; + cache->gc_ttl = gc_ttl; + cache->ttl = ttl; + CREATE_LOCK(cache->header->lock); +#if NONBLOCKING_LOCK_AVAILABLE + CREATE_LOCK(cache->header->wrlock); +#endif + memset(cache->slots, 0, sizeof(slot_t*)*num_slots); + cache->expunge_cb = apc_cache_expunge; + cache->has_lock = 0; + + return cache; +} +/* }}} */ + +/* {{{ apc_cache_destroy */ +void apc_cache_destroy(apc_cache_t* cache TSRMLS_DC) +{ + DESTROY_LOCK(cache->header->lock); +#ifdef NONBLOCKING_LOCK_AVAILABLE + DESTROY_LOCK(cache->header->wrlock); +#endif + apc_efree(cache TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_cache_clear */ +void apc_cache_clear(apc_cache_t* cache TSRMLS_DC) +{ + int i; + + if(!cache) return; + + CACHE_LOCK(cache); + cache->header->busy = 1; + cache->header->num_hits = 0; + cache->header->num_misses = 0; + cache->header->start_time = time(NULL); + cache->header->expunges = 0; + + for (i = 0; i < cache->num_slots; i++) { + slot_t* p = cache->slots[i]; + while (p) { + remove_slot(cache, &p TSRMLS_CC); + } + cache->slots[i] = NULL; + } + + memset(&cache->header->lastkey, 0, sizeof(apc_keyid_t)); + + cache->header->busy = 0; + CACHE_UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_expunge */ +static void apc_cache_expunge(apc_cache_t* cache, size_t size TSRMLS_DC) +{ + int i; + time_t t; + + t = apc_time(); + + if(!cache) return; + + if(!cache->ttl) { + /* + * If cache->ttl is not set, we wipe out the entire cache when + * we run out of space. + */ + CACHE_SAFE_LOCK(cache); + if (apc_sma_get_avail_mem() > (size_t)(APCG(shm_size)/2)) { + /* probably a queued up expunge, we don't need to do this */ + CACHE_SAFE_UNLOCK(cache); + return; + } + cache->header->busy = 1; + cache->header->expunges++; +clear_all: + for (i = 0; i < cache->num_slots; i++) { + slot_t* p = cache->slots[i]; + while (p) { + remove_slot(cache, &p TSRMLS_CC); + } + cache->slots[i] = NULL; + } + memset(&cache->header->lastkey, 0, sizeof(apc_keyid_t)); + cache->header->busy = 0; + CACHE_SAFE_UNLOCK(cache); + } else { + slot_t **p; + /* + * If the ttl for the cache is set we walk through and delete stale + * entries. For the user cache that is slightly confusing since + * we have the individual entry ttl's we can look at, but that would be + * too much work. So if you want the user cache expunged, set a high + * default apc.user_ttl and still provide a specific ttl for each entry + * on insert + */ + + CACHE_SAFE_LOCK(cache); + if (apc_sma_get_avail_mem() > (size_t)(APCG(shm_size)/2)) { + /* probably a queued up expunge, we don't need to do this */ + CACHE_SAFE_UNLOCK(cache); + return; + } + cache->header->busy = 1; + cache->header->expunges++; + for (i = 0; i < cache->num_slots; i++) { + p = &cache->slots[i]; + while(*p) { + /* + * For the user cache we look at the individual entry ttl values + * and if not set fall back to the default ttl for the user cache + */ + if((*p)->value->type == APC_CACHE_ENTRY_USER) { + if((*p)->value->data.user.ttl) { + if((time_t) ((*p)->creation_time + (*p)->value->data.user.ttl) < t) { + remove_slot(cache, p TSRMLS_CC); + continue; + } + } else if(cache->ttl) { + if((*p)->creation_time + cache->ttl < t) { + remove_slot(cache, p TSRMLS_CC); + continue; + } + } + } else if((*p)->access_time < (t - cache->ttl)) { + remove_slot(cache, p TSRMLS_CC); + continue; + } + p = &(*p)->next; + } + } + + if (!apc_sma_get_avail_size(size)) { + /* TODO: re-do this to remove goto across locked sections */ + goto clear_all; + } + memset(&cache->header->lastkey, 0, sizeof(apc_keyid_t)); + cache->header->busy = 0; + CACHE_SAFE_UNLOCK(cache); + } +} +/* }}} */ + +/* {{{ apc_cache_insert */ +static inline int _apc_cache_insert(apc_cache_t* cache, + apc_cache_key_t key, + apc_cache_entry_t* value, + apc_context_t* ctxt, + time_t t + TSRMLS_DC) +{ + slot_t** slot; + + if (!value) { + return 0; + } + + apc_debug("Inserting [%s]\n" TSRMLS_CC, value->data.file.filename); + + process_pending_removals(cache TSRMLS_CC); + + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; + + while(*slot) { + if(key.type == (*slot)->key.type) { + if(key.type == APC_CACHE_KEY_FILE) { + if(key_equals((*slot)->key.data.file, key.data.file)) { + /* If existing slot for the same device+inode is different, remove it and insert the new version */ + if (ctxt->force_update || (*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot TSRMLS_CC); + break; + } + return 0; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot TSRMLS_CC); + continue; + } + } else { /* APC_CACHE_KEY_FPFILE */ + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { + /* Hrm.. it's already here, remove it and insert new one */ + remove_slot(cache, slot TSRMLS_CC); + break; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot TSRMLS_CC); + continue; + } + } + } + slot = &(*slot)->next; + } + + if ((*slot = make_slot(key, value, *slot, t TSRMLS_CC)) == NULL) { + return -1; + } + + value->mem_size = ctxt->pool->size; + cache->header->mem_size += ctxt->pool->size; + cache->header->num_entries++; + cache->header->num_inserts++; + + return 1; +} +/* }}} */ + +/* {{{ apc_cache_insert */ +int apc_cache_insert(apc_cache_t* cache, apc_cache_key_t key, apc_cache_entry_t* value, apc_context_t *ctxt, time_t t TSRMLS_DC) +{ + int rval; + CACHE_LOCK(cache); + rval = _apc_cache_insert(cache, key, value, ctxt, t TSRMLS_CC); + CACHE_UNLOCK(cache); + return rval; +} +/* }}} */ + +/* {{{ apc_cache_insert */ +int *apc_cache_insert_mult(apc_cache_t* cache, apc_cache_key_t* keys, apc_cache_entry_t** values, apc_context_t *ctxt, time_t t, int num_entries TSRMLS_DC) +{ + int *rval; + int i; + + rval = emalloc(sizeof(int) * num_entries); + CACHE_LOCK(cache); + for (i=0; i < num_entries; i++) { + if (values[i]) { + ctxt->pool = values[i]->pool; + rval[i] = _apc_cache_insert(cache, keys[i], values[i], ctxt, t TSRMLS_CC); + } + } + CACHE_UNLOCK(cache); + return rval; +} +/* }}} */ + + +/* {{{ apc_cache_user_insert */ +int apc_cache_user_insert(apc_cache_t* cache, apc_cache_key_t key, apc_cache_entry_t* value, apc_context_t* ctxt, time_t t, int exclusive TSRMLS_DC) +{ + slot_t** slot; + unsigned int keylen = key.data.user.identifier_len; + unsigned int h = string_nhash_8(key.data.user.identifier, keylen); + apc_keyid_t *lastkey = &cache->header->lastkey; + + if (!value) { + return 0; + } + + if(apc_cache_busy(cache)) { + /* cache cleanup in progress, do not wait */ + return 0; + } + + if(apc_cache_is_last_key(cache, &key, h, t TSRMLS_CC)) { + /* potential cache slam */ + printf("Last key warning for it!"); + return 0; + } + + CACHE_LOCK(cache); + + memset(lastkey, 0, sizeof(apc_keyid_t)); + + lastkey->h = h; + lastkey->keylen = keylen; + lastkey->mtime = t; +#ifdef ZTS + lastkey->tid = tsrm_thread_id(); +#else + lastkey->pid = getpid(); +#endif + + /* we do not reset lastkey after the insert. Whether it is inserted + * or not, another insert in the same second is always a bad idea. + */ + + process_pending_removals(cache TSRMLS_CC); + + slot = &cache->slots[h % cache->num_slots]; + + while (*slot) { + if (((*slot)->key.data.user.identifier_len == key.data.user.identifier_len) && + (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, keylen))) { + /* + * At this point we have found the user cache entry. If we are doing + * an exclusive insert (apc_add) we are going to bail right away if + * the user entry already exists and it has no ttl, or + * there is a ttl and the entry has not timed out yet. + */ + if(exclusive && ( !(*slot)->value->data.user.ttl || + ( (*slot)->value->data.user.ttl && (time_t) ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t ) + ) ) { + goto fail; + } + remove_slot(cache, slot TSRMLS_CC); + break; + } else + /* + * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of + * slot entries so we don't always have to skip past a bunch of stale entries. We check + * for staleness here and get rid of them by first checking to see if the cache has a global + * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly + * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl + */ + if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) || + ((*slot)->value->data.user.ttl && (time_t) ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) { + remove_slot(cache, slot TSRMLS_CC); + continue; + } + slot = &(*slot)->next; + } + + if ((*slot = make_slot(key, value, *slot, t TSRMLS_CC)) == NULL) { + goto fail; + } + + value->mem_size = ctxt->pool->size; + cache->header->mem_size += ctxt->pool->size; + + cache->header->num_entries++; + cache->header->num_inserts++; + + CACHE_UNLOCK(cache); + + return 1; + +fail: + CACHE_UNLOCK(cache); + + return 0; +} +/* }}} */ + +/* {{{ apc_cache_find_slot */ +slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t TSRMLS_DC) +{ + slot_t** slot; + volatile slot_t* retval = NULL; + + CACHE_LOCK(cache); + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; + + while (*slot) { + if(key.type == (*slot)->key.type) { + if(key.type == APC_CACHE_KEY_FILE) { + if(key_equals((*slot)->key.data.file, key.data.file)) { + if((*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot TSRMLS_CC); + cache->header->num_misses++; + CACHE_UNLOCK(cache); + return NULL; + } + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + prevent_garbage_collection((*slot)->value); + cache->header->num_hits++; + retval = *slot; + CACHE_UNLOCK(cache); + return (slot_t*)retval; + } + } else { /* APC_CACHE_KEY_FPFILE */ + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { + /* TTL Check ? */ + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + prevent_garbage_collection((*slot)->value); + cache->header->num_hits++; + retval = *slot; + CACHE_UNLOCK(cache); + return (slot_t*)retval; + } + } + } + slot = &(*slot)->next; + } + cache->header->num_misses++; + CACHE_UNLOCK(cache); + return NULL; +} +/* }}} */ + +/* {{{ apc_cache_find */ +apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t TSRMLS_DC) +{ + slot_t * slot = apc_cache_find_slot(cache, key, t TSRMLS_CC); + return (slot) ? slot->value : NULL; +} +/* }}} */ + +/* {{{ apc_cache_user_find */ +apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t TSRMLS_DC) +{ + slot_t** slot; + volatile apc_cache_entry_t* value = NULL; + + if(apc_cache_busy(cache)) + { + /* cache cleanup in progress */ + return NULL; + } + + CACHE_LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + /* Check to make sure this entry isn't expired by a hard TTL */ + if((*slot)->value->data.user.ttl && (time_t) ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) { + remove_slot(cache, slot TSRMLS_CC); + cache->header->num_misses++; + CACHE_UNLOCK(cache); + return NULL; + } + /* Otherwise we are fine, increase counters and return the cache entry */ + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + + cache->header->num_hits++; + value = (*slot)->value; + CACHE_UNLOCK(cache); + return (apc_cache_entry_t*)value; + } + slot = &(*slot)->next; + } + + cache->header->num_misses++; + CACHE_UNLOCK(cache); + return NULL; +} +/* }}} */ + +/* {{{ apc_cache_user_exists */ +apc_cache_entry_t* apc_cache_user_exists(apc_cache_t* cache, char *strkey, int keylen, time_t t TSRMLS_DC) +{ + slot_t** slot; + volatile apc_cache_entry_t* value = NULL; + + if(apc_cache_busy(cache)) + { + /* cache cleanup in progress */ + return NULL; + } + + CACHE_LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + /* Check to make sure this entry isn't expired by a hard TTL */ + if((*slot)->value->data.user.ttl && (time_t) ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) { + CACHE_UNLOCK(cache); + return NULL; + } + /* Return the cache entry ptr */ + value = (*slot)->value; + CACHE_UNLOCK(cache); + return (apc_cache_entry_t*)value; + } + slot = &(*slot)->next; + } + CACHE_UNLOCK(cache); + return NULL; +} +/* }}} */ + +/* {{{ apc_cache_user_update */ +int _apc_cache_user_update(apc_cache_t* cache, char *strkey, int keylen, apc_cache_updater_t updater, void* data TSRMLS_DC) +{ + slot_t** slot; + int retval; + + if(apc_cache_busy(cache)) + { + /* cache cleanup in progress */ + return 0; + } + + CACHE_LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + retval = updater(cache, (*slot)->value, data); + (*slot)->key.mtime = apc_time(); + CACHE_UNLOCK(cache); + return retval; + } + slot = &(*slot)->next; + } + CACHE_UNLOCK(cache); + return 0; +} +/* }}} */ + +/* {{{ apc_cache_user_delete */ +int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen TSRMLS_DC) +{ + slot_t** slot; + + CACHE_LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + remove_slot(cache, slot TSRMLS_CC); + CACHE_UNLOCK(cache); + return 1; + } + slot = &(*slot)->next; + } + + CACHE_UNLOCK(cache); + return 0; +} +/* }}} */ + +/* {{{ apc_cache_delete */ +int apc_cache_delete(apc_cache_t* cache, char *filename, int filename_len TSRMLS_DC) +{ + slot_t** slot; + time_t t; + apc_cache_key_t key; + + t = apc_time(); + + /* try to create a cache key; if we fail, give up on caching */ + if (!apc_cache_make_file_key(&key, filename, PG(include_path), t TSRMLS_CC)) { + apc_warning("Could not stat file %s, unable to delete from cache." TSRMLS_CC, filename); + return -1; + } + + CACHE_LOCK(cache); + + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; + + while(*slot) { + if(key.type == (*slot)->key.type) { + if(key.type == APC_CACHE_KEY_FILE) { + if(key_equals((*slot)->key.data.file, key.data.file)) { + remove_slot(cache, slot TSRMLS_CC); + CACHE_UNLOCK(cache); + return 1; + } + } else { /* APC_CACHE_KEY_FPFILE */ + if(((*slot)->key.data.fpfile.fullpath_len == key.data.fpfile.fullpath_len) && + (!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1))) { + remove_slot(cache, slot TSRMLS_CC); + CACHE_UNLOCK(cache); + return 1; + } + } + } + slot = &(*slot)->next; + } + + memset(&cache->header->lastkey, 0, sizeof(apc_keyid_t)); + + CACHE_UNLOCK(cache); + return 0; + +} +/* }}} */ + +/* {{{ apc_cache_release */ +void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry TSRMLS_DC) +{ + CACHE_LOCK(cache); + entry->ref_count--; + CACHE_UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_make_file_key */ +int apc_cache_make_file_key(apc_cache_key_t* key, + const char* filename, + const char* include_path, + time_t t + TSRMLS_DC) +{ + struct stat *tmp_buf=NULL; + struct apc_fileinfo_t *fileinfo = NULL; + int len; + + assert(key != NULL); + + if (!filename || !SG(request_info).path_translated) { + apc_debug("No filename and no path_translated - bailing\n" TSRMLS_CC); + goto cleanup; + } + + len = strlen(filename); + if(APCG(fpstat)==0) { + if(IS_ABSOLUTE_PATH(filename,len)) { + key->data.fpfile.fullpath = filename; + key->data.fpfile.fullpath_len = len; + key->mtime = t; + key->type = APC_CACHE_KEY_FPFILE; + goto success; + } else if(APCG(canonicalize)) { + + fileinfo = apc_php_malloc(sizeof(apc_fileinfo_t) TSRMLS_CC); + + if (apc_search_paths(filename, include_path, fileinfo TSRMLS_CC) != 0) { + apc_warning("apc failed to locate %s - bailing" TSRMLS_CC, filename); + goto cleanup; + } + + if(!VCWD_REALPATH(fileinfo->fullpath, APCG(canon_path))) { + apc_warning("realpath failed to canonicalize %s - bailing" TSRMLS_CC, filename); + goto cleanup; + } + + key->data.fpfile.fullpath = APCG(canon_path); + key->data.fpfile.fullpath_len = strlen(APCG(canon_path)); + key->mtime = t; + key->type = APC_CACHE_KEY_FPFILE; + goto success; + } + /* fall through to stat mode */ + } + + fileinfo = apc_php_malloc(sizeof(apc_fileinfo_t) TSRMLS_CC); + + assert(fileinfo != NULL); + + if(!strcmp(SG(request_info).path_translated, filename)) { + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ + } + + if(tmp_buf) { + fileinfo->st_buf.sb = *tmp_buf; + } else { + if (apc_search_paths(filename, include_path, fileinfo TSRMLS_CC) != 0) { + apc_debug("Stat failed %s - bailing (%s) (%d)\n" TSRMLS_CC, filename,SG(request_info).path_translated); + goto cleanup; + } + } + + if(APCG(max_file_size) < fileinfo->st_buf.sb.st_size) { + apc_debug("File is too big %s (%d - %ld) - bailing\n" TSRMLS_CC, filename,t,fileinfo->st_buf.sb.st_size); + goto cleanup; + } + + /* + * This is a bit of a hack. + * + * Here I am checking to see if the file is at least 2 seconds old. + * The idea is that if the file is currently being written to then its + * mtime is going to match or at most be 1 second off of the current + * request time and we want to avoid caching files that have not been + * completely written. Of course, people should be using atomic + * mechanisms to push files onto live web servers, but adding this + * tiny safety is easier than educating the world. This is now + * configurable, but the default is still 2 seconds. + */ + if(APCG(file_update_protection) && (t - fileinfo->st_buf.sb.st_mtime < APCG(file_update_protection)) && !APCG(force_file_update)) { + apc_debug("File is too new %s (%d - %d) - bailing\n" TSRMLS_CC,filename,t,fileinfo->st_buf.sb.st_mtime); + goto cleanup; + } + + key->data.file.device = fileinfo->st_buf.sb.st_dev; + key->data.file.inode = fileinfo->st_buf.sb.st_ino; + + /* + * If working with content management systems that like to munge the mtime, + * it might be appropriate to key off of the ctime to be immune to systems + * that try to backdate a template. If the mtime is set to something older + * than the previous mtime of a template we will obviously never see this + * "older" template. At some point the Smarty templating system did this. + * I generally disagree with using the ctime here because you lose the + * ability to warm up new content by saving it to a temporary file, hitting + * it once to cache it and then renaming it into its permanent location so + * set the apc.stat_ctime=true to enable this check. + */ + if(APCG(stat_ctime)) { + key->mtime = (fileinfo->st_buf.sb.st_ctime > fileinfo->st_buf.sb.st_mtime) ? fileinfo->st_buf.sb.st_ctime : fileinfo->st_buf.sb.st_mtime; + } else { + key->mtime = fileinfo->st_buf.sb.st_mtime; + } + key->type = APC_CACHE_KEY_FILE; + +success: + + if(fileinfo != NULL) { + apc_php_free(fileinfo TSRMLS_CC); + } + + return 1; + +cleanup: + + if(fileinfo != NULL) { + apc_php_free(fileinfo TSRMLS_CC); + } + + return 0; +} +/* }}} */ + +/* {{{ apc_cache_make_user_key */ +int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t) +{ + assert(key != NULL); + + if (!identifier) + return 0; + + key->data.user.identifier = identifier; + key->data.user.identifier_len = identifier_len; + key->mtime = t; + key->type = APC_CACHE_KEY_USER; + return 1; +} +/* }}} */ + +/* {{{ apc_cache_make_file_entry */ +apc_cache_entry_t* apc_cache_make_file_entry(const char* filename, + zend_op_array* op_array, + apc_function_t* functions, + apc_class_t* classes, + apc_context_t* ctxt + TSRMLS_DC) +{ + apc_cache_entry_t* entry; + apc_pool* pool = ctxt->pool; + + entry = (apc_cache_entry_t*) apc_pool_alloc(pool, sizeof(apc_cache_entry_t)); + if (!entry) return NULL; + + entry->data.file.filename = apc_pstrdup(filename, pool TSRMLS_CC); + if(!entry->data.file.filename) { + apc_debug("apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n" TSRMLS_CC); + return NULL; + } + apc_debug("apc_cache_make_file_entry: entry->data.file.filename is [%s]\n" TSRMLS_CC,entry->data.file.filename); + entry->data.file.op_array = op_array; + entry->data.file.functions = functions; + entry->data.file.classes = classes; + + entry->data.file.halt_offset = apc_file_halt_offset(filename TSRMLS_CC); + + entry->type = APC_CACHE_ENTRY_FILE; + entry->ref_count = 0; + entry->mem_size = 0; + entry->pool = pool; + return entry; +} +/* }}} */ + +/* {{{ apc_cache_store_zval */ +zval* apc_cache_store_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + if (Z_TYPE_P(src) == IS_ARRAY) { + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + zend_hash_init(&APCG(copied_zvals), 0, NULL, NULL, 0); + dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC); + zend_hash_destroy(&APCG(copied_zvals)); + APCG(copied_zvals).nTableSize=0; + } else { + dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC); + } + + + return dst; +} +/* }}} */ + +/* {{{ apc_cache_fetch_zval */ +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + if (Z_TYPE_P(src) == IS_ARRAY) { + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + zend_hash_init(&APCG(copied_zvals), 0, NULL, NULL, 0); + dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC); + zend_hash_destroy(&APCG(copied_zvals)); + APCG(copied_zvals).nTableSize=0; + } else { + dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC); + } + + + return dst; +} +/* }}} */ + +/* {{{ apc_cache_make_user_entry */ +apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, apc_context_t* ctxt, const unsigned int ttl TSRMLS_DC) +{ + apc_cache_entry_t* entry; + apc_pool* pool = ctxt->pool; + + entry = (apc_cache_entry_t*) apc_pool_alloc(pool, sizeof(apc_cache_entry_t)); + if (!entry) return NULL; + + entry->data.user.info = apc_pmemcpy(info, info_len, pool TSRMLS_CC); + entry->data.user.info_len = info_len; + if(!entry->data.user.info) { + return NULL; + } + entry->data.user.val = apc_cache_store_zval(NULL, val, ctxt TSRMLS_CC); + if(!entry->data.user.val) { + return NULL; + } + INIT_PZVAL(entry->data.user.val); + entry->data.user.ttl = ttl; + entry->type = APC_CACHE_ENTRY_USER; + entry->ref_count = 0; + entry->mem_size = 0; + entry->pool = pool; + return entry; +} +/* }}} */ + +/* {{{ apc_cache_info */ +apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited TSRMLS_DC) +{ + apc_cache_info_t* info; + slot_t* p; + int i; + + if(!cache) return NULL; + + CACHE_LOCK(cache); + + info = (apc_cache_info_t*) apc_php_malloc(sizeof(apc_cache_info_t) TSRMLS_CC); + if(!info) { + CACHE_UNLOCK(cache); + return NULL; + } + info->num_slots = cache->num_slots; + info->ttl = cache->ttl; + info->num_hits = cache->header->num_hits; + info->num_misses = cache->header->num_misses; + info->list = NULL; + info->deleted_list = NULL; + info->start_time = cache->header->start_time; + info->expunges = cache->header->expunges; + info->mem_size = cache->header->mem_size; + info->num_entries = cache->header->num_entries; + info->num_inserts = cache->header->num_inserts; + + if(!limited) { + /* For each hashtable slot */ + for (i = 0; i < info->num_slots; i++) { + p = cache->slots[i]; + for (; p != NULL; p = p->next) { + apc_cache_link_t* link = (apc_cache_link_t*) apc_php_malloc(sizeof(apc_cache_link_t) TSRMLS_CC); + + if(p->value->type == APC_CACHE_ENTRY_FILE) { + if(p->key.type == APC_CACHE_KEY_FILE) { + link->data.file.device = p->key.data.file.device; + link->data.file.inode = p->key.data.file.inode; + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_php_malloc TSRMLS_CC); + } else { /* This is a no-stat fullpath file entry */ + link->data.file.device = 0; + link->data.file.inode = 0; + link->data.file.filename = apc_xstrdup(p->key.data.fpfile.fullpath, apc_php_malloc TSRMLS_CC); + } + link->type = APC_CACHE_ENTRY_FILE; + if (APCG(file_md5)) { + link->data.file.md5 = emalloc(sizeof(p->key.md5)); + memcpy(link->data.file.md5, p->key.md5, 16); + } else { + link->data.file.md5 = NULL; + } + } else if(p->value->type == APC_CACHE_ENTRY_USER) { + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len+1, apc_php_malloc TSRMLS_CC); + link->data.user.ttl = p->value->data.user.ttl; + link->type = APC_CACHE_ENTRY_USER; + } + link->num_hits = p->num_hits; + link->mtime = p->key.mtime; + link->creation_time = p->creation_time; + link->deletion_time = p->deletion_time; + link->access_time = p->access_time; + link->ref_count = p->value->ref_count; + link->mem_size = p->value->mem_size; + link->next = info->list; + info->list = link; + } + } + + /* For each slot pending deletion */ + for (p = cache->header->deleted_list; p != NULL; p = p->next) { + apc_cache_link_t* link = (apc_cache_link_t*) apc_php_malloc(sizeof(apc_cache_link_t) TSRMLS_CC); + + if(p->value->type == APC_CACHE_ENTRY_FILE) { + if(p->key.type == APC_CACHE_KEY_FILE) { + link->data.file.device = p->key.data.file.device; + link->data.file.inode = p->key.data.file.inode; + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_php_malloc TSRMLS_CC); + } else { /* This is a no-stat fullpath file entry */ + link->data.file.device = 0; + link->data.file.inode = 0; + link->data.file.filename = apc_xstrdup(p->key.data.fpfile.fullpath, apc_php_malloc TSRMLS_CC); + } + link->type = APC_CACHE_ENTRY_FILE; + if (APCG(file_md5)) { + link->data.file.md5 = emalloc(sizeof(p->key.md5)); + memcpy(link->data.file.md5, p->key.md5, 16); + } else { + link->data.file.md5 = NULL; + } + } else if(p->value->type == APC_CACHE_ENTRY_USER) { + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len+1, apc_php_malloc TSRMLS_CC); + link->data.user.ttl = p->value->data.user.ttl; + link->type = APC_CACHE_ENTRY_USER; + } + link->num_hits = p->num_hits; + link->mtime = p->key.mtime; + link->creation_time = p->creation_time; + link->deletion_time = p->deletion_time; + link->access_time = p->access_time; + link->ref_count = p->value->ref_count; + link->mem_size = p->value->mem_size; + link->next = info->deleted_list; + info->deleted_list = link; + } + } + + CACHE_UNLOCK(cache); + return info; +} +/* }}} */ + +/* {{{ apc_cache_free_info */ +void apc_cache_free_info(apc_cache_info_t* info TSRMLS_DC) +{ + apc_cache_link_t* p = info->list; + apc_cache_link_t* q = NULL; + while (p != NULL) { + q = p; + p = p->next; + if(q->type == APC_CACHE_ENTRY_FILE) { + if(q->data.file.md5) { + efree(q->data.file.md5); + } + apc_php_free(q->data.file.filename TSRMLS_CC); + } + else if(q->type == APC_CACHE_ENTRY_USER) apc_php_free(q->data.user.info TSRMLS_CC); + apc_php_free(q TSRMLS_CC); + } + p = info->deleted_list; + while (p != NULL) { + q = p; + p = p->next; + if(q->type == APC_CACHE_ENTRY_FILE) { + if(q->data.file.md5) { + efree(q->data.file.md5); + } + apc_php_free(q->data.file.filename TSRMLS_CC); + } + else if(q->type == APC_CACHE_ENTRY_USER) apc_php_free(q->data.user.info TSRMLS_CC); + apc_php_free(q TSRMLS_CC); + } + apc_php_free(info TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_cache_unlock */ +void apc_cache_unlock(apc_cache_t* cache TSRMLS_DC) +{ + CACHE_UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_busy */ +zend_bool apc_cache_busy(apc_cache_t* cache) +{ + return cache->header->busy; +} +/* }}} */ + +/* {{{ apc_cache_is_last_key */ +zend_bool apc_cache_is_last_key(apc_cache_t* cache, apc_cache_key_t* key, unsigned int h, time_t t TSRMLS_DC) +{ + apc_keyid_t *lastkey = &cache->header->lastkey; + unsigned int keylen = key->data.user.identifier_len; +#ifdef ZTS + THREAD_T tid = tsrm_thread_id(); + #define FROM_DIFFERENT_THREAD(k) (memcmp(&((k)->tid), &tid, sizeof(THREAD_T))!=0) +#else + pid_t pid = getpid(); + #define FROM_DIFFERENT_THREAD(k) (pid != (k)->pid) +#endif + + + if(!h) h = string_nhash_8(key->data.user.identifier, keylen); + + /* unlocked reads, but we're not shooting for 100% success with this */ + if(lastkey->h == h && keylen == lastkey->keylen) { + if(lastkey->mtime == t && FROM_DIFFERENT_THREAD(lastkey)) { + /* potential cache slam */ + if(APCG(slam_defense)) { + apc_warning("Potential cache slam averted for key '%s'" TSRMLS_CC, key->data.user.identifier); + return 1; + } + } + } + + return 0; +} +/* }}} */ + +#if NONBLOCKING_LOCK_AVAILABLE +/* {{{ apc_cache_write_lock */ +zend_bool apc_cache_write_lock(apc_cache_t* cache TSRMLS_DC) +{ + return apc_lck_nb_lock(cache->header->wrlock); +} +/* }}} */ + +/* {{{ apc_cache_write_unlock */ +void apc_cache_write_unlock(apc_cache_t* cache TSRMLS_DC) +{ + apc_lck_unlock(cache->header->wrlock); +} +/* }}} */ +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_cache.h @@ -0,0 +1,403 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_cache.h 305258 2010-11-10 19:02:06Z gopalv $ */ + +#ifndef APC_CACHE_H +#define APC_CACHE_H + +/* + * This module defines the shared memory file cache. Basically all of the + * logic for storing and retrieving cache entries lives here. + */ + +#include "apc.h" +#include "apc_compile.h" +#include "apc_lock.h" +#include "apc_pool.h" +#include "apc_main.h" +#include "TSRM.h" + +#define APC_CACHE_ENTRY_FILE 1 +#define APC_CACHE_ENTRY_USER 2 + +#define APC_CACHE_KEY_FILE 1 +#define APC_CACHE_KEY_USER 2 +#define APC_CACHE_KEY_FPFILE 3 + +#ifdef PHP_WIN32 +typedef unsigned __int64 apc_ino_t; +typedef unsigned __int64 apc_dev_t; +#else +typedef ino_t apc_ino_t; +typedef dev_t apc_dev_t; +#endif + +/* {{{ cache locking macros */ +#define CACHE_LOCK(cache) { LOCK(cache->header->lock); cache->has_lock = 1; } +#define CACHE_UNLOCK(cache) { UNLOCK(cache->header->lock); cache->has_lock = 0; } +#define CACHE_SAFE_LOCK(cache) { if ((++cache->has_lock) == 1) LOCK(cache->header->lock); } +#define CACHE_SAFE_UNLOCK(cache) { if ((--cache->has_lock) == 0) UNLOCK(cache->header->lock); } +/* }}} */ + +/* {{{ struct definition: apc_cache_key_t */ +#define T apc_cache_t* +typedef struct apc_cache_t apc_cache_t; /* opaque cache type */ + +typedef union _apc_cache_key_data_t { + struct { + apc_dev_t device; /* the filesystem device */ + apc_ino_t inode; /* the filesystem inode */ + } file; + struct { + const char *identifier; + int identifier_len; + } user; + struct { + const char *fullpath; + int fullpath_len; + } fpfile; +} apc_cache_key_data_t; + +typedef struct apc_cache_key_t apc_cache_key_t; +struct apc_cache_key_t { + apc_cache_key_data_t data; + time_t mtime; /* the mtime of this cached entry */ + unsigned char type; + unsigned char md5[16]; /* md5 hash of the source file */ +}; + + +typedef struct apc_keyid_t apc_keyid_t; + +struct apc_keyid_t { + unsigned int h; + unsigned int keylen; + time_t mtime; +#ifdef ZTS + THREAD_T tid; +#else + pid_t pid; +#endif +}; +/* }}} */ + +/* {{{ struct definition: apc_cache_entry_t */ +typedef union _apc_cache_entry_value_t { + struct { + char *filename; /* absolute path to source file */ + zend_op_array* op_array; /* op_array allocated in shared memory */ + apc_function_t* functions; /* array of apc_function_t's */ + apc_class_t* classes; /* array of apc_class_t's */ + long halt_offset; /* value of __COMPILER_HALT_OFFSET__ for the file */ + } file; + struct { + char *info; + int info_len; + zval *val; + unsigned int ttl; + } user; +} apc_cache_entry_value_t; + +typedef struct apc_cache_entry_t apc_cache_entry_t; +struct apc_cache_entry_t { + apc_cache_entry_value_t data; + unsigned char type; + int ref_count; + size_t mem_size; + apc_pool *pool; +}; +/* }}} */ + +/* + * apc_cache_create creates the shared memory compiler cache. This function + * should be called just once (ideally in the web server parent process, e.g. + * in apache), otherwise you will end up with multiple caches (which won't + * necessarily break anything). Returns a pointer to the cache object. + * + * size_hint is a "hint" at the total number of source files that will be + * cached. It determines the physical size of the hash table. Passing 0 for + * this argument will use a reasonable default value. + * + * gc_ttl is the maximum time a cache entry may speed on the garbage + * collection list. This is basically a work around for the inherent + * unreliability of our reference counting mechanism (see apc_cache_release). + * + * ttl is the maximum time a cache entry can idle in a slot in case the slot + * is needed. This helps in cleaning up the cache and ensuring that entries + * hit frequently stay cached and ones not hit very often eventually disappear. + */ +extern T apc_cache_create(int size_hint, int gc_ttl, int ttl TSRMLS_DC); + +/* + * apc_cache_destroy releases any OS resources associated with a cache object. + * Under apache, this function can be safely called by the child processes + * when they exit. + */ +extern void apc_cache_destroy(T cache TSRMLS_DC); + +/* + * apc_cache_clear empties a cache. This can safely be called at any time, + * even while other server processes are executing cached source files. + */ +extern void apc_cache_clear(T cache TSRMLS_DC); + +/* + * apc_cache_insert adds an entry to the cache, using a filename as a key. + * Internally, the filename is translated to a canonical representation, so + * that relative and absolute filenames will map to a single key. Returns + * non-zero if the file was successfully inserted, 0 otherwise. If 0 is + * returned, the caller must free the cache entry by calling + * apc_cache_free_entry (see below). + * + * key is the value created by apc_cache_make_file_key for file keys. + * + * value is a cache entry returned by apc_cache_make_entry (see below). + */ +extern int apc_cache_insert(T cache, apc_cache_key_t key, + apc_cache_entry_t* value, apc_context_t* ctxt, time_t t TSRMLS_DC); + +extern int apc_cache_user_insert(T cache, apc_cache_key_t key, + apc_cache_entry_t* value, apc_context_t* ctxt, time_t t, int exclusive TSRMLS_DC); + +extern int *apc_cache_insert_mult(apc_cache_t* cache, apc_cache_key_t* keys, + apc_cache_entry_t** values, apc_context_t *ctxt, time_t t, int num_entries TSRMLS_DC); + +/* + * apc_cache_find searches for a cache entry by filename, and returns a + * pointer to the entry if found, NULL otherwise. + * + * key is a value created by apc_cache_make_file_key for file keys. + */ +extern apc_cache_entry_t* apc_cache_find(T cache, apc_cache_key_t key, time_t t TSRMLS_DC); + +/* + * apc_cache_user_find searches for a cache entry by its hashed identifier, + * and returns a pointer to the entry if found, NULL otherwise. + * + */ +extern apc_cache_entry_t* apc_cache_user_find(T cache, char* strkey, int keylen, time_t t TSRMLS_DC); + +/* + * apc_cache_user_exists searches for a cache entry by its hashed identifier, + * and returns a pointer to the entry if found, NULL otherwise. This is a + * quick non-locking version of apc_cache_user_find that does not modify the + * shared memory segment in any way. + * + */ +extern apc_cache_entry_t* apc_cache_user_exists(T cache, char* strkey, int keylen, time_t t TSRMLS_DC); + +/* + * apc_cache_delete and apc_cache_user_delete finds an entry in the cache and deletes it. + */ +extern int apc_cache_delete(apc_cache_t* cache, char *filename, int filename_len TSRMLS_DC); +extern int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen TSRMLS_DC); + +/* apc_cach_fetch_zval takes a zval in the cache and reconstructs a runtime + * zval from it. + * + */ +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC); + +/* + * apc_cache_release decrements the reference count associated with a cache + * entry. Calling apc_cache_find automatically increments the reference count, + * and this function must be called post-execution to return the count to its + * original value. Failing to do so will prevent the entry from being + * garbage-collected. + * + * entry is the cache entry whose ref count you want to decrement. + */ +extern void apc_cache_release(T cache, apc_cache_entry_t* entry TSRMLS_DC); + +/* + * apc_cache_make_file_key creates a key object given a relative or absolute + * filename and an optional list of auxillary paths to search. include_path is + * searched if the filename cannot be found relative to the current working + * directory. + * + * key points to caller-allocated storage (must not be null). + * + * filename is the path to the source file. + * + * include_path is a colon-separated list of directories to search. + * + * and finally we pass in the current request time so we can avoid + * caching files with a current mtime which tends to indicate that + * they are still being written to. + */ +extern int apc_cache_make_file_key(apc_cache_key_t* key, + const char* filename, + const char* include_path, + time_t t + TSRMLS_DC); + +/* + * apc_cache_make_file_entry creates an apc_cache_entry_t object given a filename + * and the compilation results returned by the PHP compiler. + */ +extern apc_cache_entry_t* apc_cache_make_file_entry(const char* filename, + zend_op_array* op_array, + apc_function_t* functions, + apc_class_t* classes, + apc_context_t* ctxt + TSRMLS_DC); + + +zend_bool apc_compile_cache_entry(apc_cache_key_t key, zend_file_handle* h, int type, time_t t, zend_op_array** op_array_pp, apc_cache_entry_t** cache_entry_pp TSRMLS_DC); + +/* + * apc_cache_make_user_entry creates an apc_cache_entry_t object given an info string + * and the zval to be stored. + */ +extern apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval *val, apc_context_t* ctxt, const unsigned int ttl TSRMLS_DC); + +extern int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t); + +/* {{{ struct definition: apc_cache_link_data_t */ +typedef union _apc_cache_link_data_t { + struct { + char *filename; + apc_ino_t device; + apc_dev_t inode; + unsigned char *md5; + } file; + struct { + char *info; + unsigned int ttl; + } user; +} apc_cache_link_data_t; +/* }}} */ + +/* {{{ struct definition: apc_cache_link_t */ +typedef struct apc_cache_link_t apc_cache_link_t; +struct apc_cache_link_t { + apc_cache_link_data_t data; + unsigned char type; + unsigned long num_hits; + time_t mtime; + time_t creation_time; + time_t deletion_time; + time_t access_time; + int ref_count; + size_t mem_size; + apc_cache_link_t* next; +}; +/* }}} */ + + +/* {{{ struct definition: apc_cache_info_t */ +typedef struct apc_cache_info_t apc_cache_info_t; +struct apc_cache_info_t { + int num_slots; + unsigned long num_hits; + unsigned long num_misses; + unsigned long num_inserts; + unsigned long expunges; + int ttl; + apc_cache_link_t* list; + apc_cache_link_t* deleted_list; + time_t start_time; + int num_entries; + size_t mem_size; +}; +/* }}} */ + +/* {{{ struct definition: slot_t */ +typedef struct slot_t slot_t; +struct slot_t { + apc_cache_key_t key; /* slot key */ + apc_cache_entry_t* value; /* slot value */ + slot_t* next; /* next slot in linked list */ + unsigned long num_hits; /* number of hits to this bucket */ + time_t creation_time; /* time slot was initialized */ + time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ +}; +/* }}} */ + +/* {{{ struct definition: cache_header_t + Any values that must be shared among processes should go in here. */ +typedef struct cache_header_t cache_header_t; +struct cache_header_t { + apc_lck_t lock; /* read/write lock (exclusive blocking cache lock) */ + apc_lck_t wrlock; /* write lock (non-blocking used to prevent cache slams) */ + unsigned long num_hits; /* total successful hits in cache */ + unsigned long num_misses; /* total unsuccessful hits in cache */ + unsigned long num_inserts; /* total successful inserts in cache */ + unsigned long expunges; /* total number of expunges */ + slot_t* deleted_list; /* linked list of to-be-deleted slots */ + time_t start_time; /* time the above counters were reset */ + zend_bool busy; /* Flag to tell clients when we are busy cleaning the cache */ + int num_entries; /* Statistic on the number of entries */ + size_t mem_size; /* Statistic on the memory size used by this cache */ + apc_keyid_t lastkey; /* the key that is being inserted (user cache) */ +}; +/* }}} */ + +typedef void (*apc_expunge_cb_t)(T cache, size_t n TSRMLS_DC); + +/* {{{ struct definition: apc_cache_t */ +struct apc_cache_t { + void* shmaddr; /* process (local) address of shared cache */ + cache_header_t* header; /* cache header (stored in SHM) */ + slot_t** slots; /* array of cache slots (stored in SHM) */ + int num_slots; /* number of slots in cache */ + int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ + apc_expunge_cb_t expunge_cb; /* cache specific expunge callback to free up sma memory */ + uint has_lock; /* flag for possible recursive locks within the same process */ +}; +/* }}} */ + +extern apc_cache_info_t* apc_cache_info(T cache, zend_bool limited TSRMLS_DC); +extern void apc_cache_free_info(apc_cache_info_t* info TSRMLS_DC); +extern void apc_cache_unlock(apc_cache_t* cache TSRMLS_DC); +extern zend_bool apc_cache_busy(apc_cache_t* cache); +extern zend_bool apc_cache_write_lock(apc_cache_t* cache TSRMLS_DC); +extern void apc_cache_write_unlock(apc_cache_t* cache TSRMLS_DC); +extern zend_bool apc_cache_is_last_key(apc_cache_t* cache, apc_cache_key_t* key, unsigned int h, time_t t TSRMLS_DC); + +/* used by apc_rfc1867 to update data in-place - not to be used elsewhere */ + +typedef int (*apc_cache_updater_t)(apc_cache_t*, apc_cache_entry_t*, void* data); +extern int _apc_cache_user_update(apc_cache_t* cache, char *strkey, int keylen, + apc_cache_updater_t updater, void* data TSRMLS_DC); + + +#undef T +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_compile.c @@ -0,0 +1,2102 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_compile.c 303796 2010-09-27 17:14:18Z gopalv $ */ + +#include "apc_compile.h" +#include "apc_globals.h" +#include "apc_zend.h" +#include "apc_string.h" +#include "ext/standard/php_var.h" +#include "ext/standard/php_smart_str.h" + +#ifndef IS_CONSTANT_TYPE_MASK +#define IS_CONSTANT_TYPE_MASK (~IS_CONSTANT_INDEX) +#endif + +typedef void* (*ht_copy_fun_t)(void*, void*, apc_context_t* TSRMLS_DC); +//typedef void (*ht_free_fun_t)(void*, apc_context_t*); +typedef int (*ht_check_copy_fun_t)(Bucket*, va_list); + +typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*); + +#define CHECK(p) { if ((p) == NULL) return NULL; } + +/* {{{ internal function declarations */ + +static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_context_t* TSRMLS_DC); + +/* + * The "copy" functions perform deep-copies on a particular data structure + * (passed as the second argument). They also optionally allocate space for + * the destination data structure if the first argument is null. + */ +static zval** my_copy_zval_ptr(zval**, const zval**, apc_context_t* TSRMLS_DC); +static zval* my_copy_zval(zval*, const zval*, apc_context_t* TSRMLS_DC); +static znode* my_copy_znode(znode*, znode*, apc_context_t* TSRMLS_DC); +static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_context_t* TSRMLS_DC); +static zend_function* my_copy_function(zend_function*, zend_function*, apc_context_t* TSRMLS_DC); +static zend_function_entry* my_copy_function_entry(zend_function_entry*, const zend_function_entry*, apc_context_t* TSRMLS_DC); +static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_context_t* TSRMLS_DC); +static HashTable* my_copy_hashtable_ex(HashTable*, HashTable* TSRMLS_DC, ht_copy_fun_t, int, apc_context_t*, ht_check_copy_fun_t, ...); +#define my_copy_hashtable( dst, src, copy_fn, holds_ptr, ctxt) \ + my_copy_hashtable_ex(dst, src TSRMLS_CC, copy_fn, holds_ptr, ctxt, NULL) +static HashTable* my_copy_static_variables(zend_op_array* src, apc_context_t* TSRMLS_DC); +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_context_t* TSRMLS_DC); +static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, const zend_arg_info*, uint, apc_context_t* TSRMLS_DC); +static zend_arg_info* my_copy_arg_info(zend_arg_info*, const zend_arg_info*, apc_context_t* TSRMLS_DC); + +/* + * The "fixup" functions need for ZEND_ENGINE_2 + */ +static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst ); +static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst ); +/* my_fixup_function_for_execution is the same as my_fixup_function + * but named differently for clarity + */ +#define my_fixup_function_for_execution my_fixup_function + +#ifdef ZEND_ENGINE_2_2 +static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst ); +#define my_fixup_property_info_for_execution my_fixup_property_info +#endif + +/* + * These functions return "1" if the member/function is + * defined/overridden in the 'current' class and not inherited. + */ +static int my_check_copy_function(Bucket* src, va_list args); +static int my_check_copy_default_property(Bucket* p, va_list args); +static int my_check_copy_property_info(Bucket* src, va_list args); +static int my_check_copy_static_member(Bucket* src, va_list args); +static int my_check_copy_constant(Bucket* src, va_list args); + +/* }}} */ + +/* {{{ check_op_array_integrity */ +#if 0 +static void check_op_array_integrity(zend_op_array* src) +{ + int i, j; + + /* These sorts of checks really aren't particularly effective, but they + * can provide a welcome sanity check when debugging. Just don't enable + * for production use! */ + + assert(src->refcount != NULL); + assert(src->opcodes != NULL); + assert(src->last > 0); + + for (i = 0; i < src->last; i++) { + zend_op* op = &src->opcodes[i]; + znode* nodes[] = { &op->result, &op->op1, &op->op2 }; + for (j = 0; j < 3; j++) { + assert(nodes[j]->op_type == IS_CONST || + nodes[j]->op_type == IS_VAR || + nodes[j]->op_type == IS_TMP_VAR || + nodes[j]->op_type == IS_UNUSED); + + if (nodes[j]->op_type == IS_CONST) { + int type = nodes[j]->u.constant.type; + assert(type == IS_RESOURCE || + type == IS_BOOL || + type == IS_LONG || + type == IS_DOUBLE || + type == IS_NULL || + type == IS_CONSTANT || + type == IS_STRING || + type == FLAG_IS_BC || + type == IS_ARRAY || + type == IS_CONSTANT_ARRAY || + type == IS_OBJECT); + } + } + } +} +#endif +/* }}} */ + +/* {{{ my_bitwise_copy_function */ +static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_context_t* ctxt TSRMLS_DC) +{ + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_function*) apc_pool_alloc(pool, sizeof(src[0]))); + } + + /* We only need to do a bitwise copy */ + memcpy(dst, src, sizeof(src[0])); + + return dst; +} +/* }}} */ + +/* {{{ my_copy_zval_ptr */ +static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_context_t* ctxt TSRMLS_DC) +{ + zval* dst_new; + apc_pool* pool = ctxt->pool; + int usegc = (ctxt->copy == APC_COPY_OUT_OPCODE) || (ctxt->copy == APC_COPY_OUT_USER); + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zval**) apc_pool_alloc(pool, sizeof(zval*))); + } + + if(usegc) { + ALLOC_ZVAL(dst[0]); + CHECK(dst[0]); + } else { + CHECK((dst[0] = (zval*) apc_pool_alloc(pool, sizeof(zval)))); + } + + CHECK((dst_new = my_copy_zval(*dst, *src, ctxt TSRMLS_CC))); + + if(dst_new != *dst) { + if(usegc) { + FREE_ZVAL(dst[0]); + } + *dst = dst_new; + } + + return dst; +} +/* }}} */ + +/* {{{ my_serialize_object */ +static zval* my_serialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + smart_str buf = {0}; + php_serialize_data_t var_hash; + apc_pool* pool = ctxt->pool; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if(buf.c) { + dst->type = src->type & ~IS_CONSTANT_INDEX; + dst->value.str.len = buf.len; + CHECK(dst->value.str.val = apc_pmemcpy(buf.c, (buf.len + 1), pool TSRMLS_CC)); + dst->type = src->type; + smart_str_free(&buf); + } + + return dst; +} +/* }}} */ + +/* {{{ my_unserialize_object */ +static zval* my_unserialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + php_unserialize_data_t var_hash; + const unsigned char *p = (unsigned char*)Z_STRVAL_P(src); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + zval_dtor(dst); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - Z_STRVAL_P(src)), Z_STRLEN_P(src)); + dst->type = IS_NULL; + } + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return dst; +} +/* }}} */ + +static char *apc_string_pmemcpy(char *str, size_t len, apc_pool* pool TSRMLS_DC) +{ +#ifdef ZEND_ENGINE_2_4 + if (pool->type != APC_UNPOOL) { + char * ret = apc_new_interned_string(str, len TSRMLS_CC); + if (ret) { + return ret; + } + } +#endif + return apc_pmemcpy(str, len, pool TSRMLS_CC); +} + +/* {{{ my_copy_zval */ +static zval* my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + zval **tmp; + apc_pool* pool = ctxt->pool; + + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + + if(APCG(copied_zvals).nTableSize) { + if(zend_hash_index_find(&APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) { + if(Z_ISREF_P((zval*)src)) { + Z_SET_ISREF_PP(tmp); + } + Z_ADDREF_PP(tmp); + return *tmp; + } + + zend_hash_index_update(&APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL); + } + + + if(ctxt->copy == APC_COPY_OUT_USER || ctxt->copy == APC_COPY_IN_USER) { + /* deep copies are refcount(1), but moved up for recursive + * arrays, which end up being add_ref'd during its copy. */ + Z_SET_REFCOUNT_P(dst, 1); + Z_UNSET_ISREF_P(dst); + } else { + /* code uses refcount=2 for consts */ + Z_SET_REFCOUNT_P(dst, Z_REFCOUNT_P((zval*)src)); + Z_SET_ISREF_TO_P(dst, Z_ISREF_P((zval*)src)); + } + + switch (src->type & IS_CONSTANT_TYPE_MASK) { + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: + case IS_DOUBLE: + case IS_NULL: + break; + + case IS_CONSTANT: + case IS_STRING: + if (src->value.str.val) { + CHECK(dst->value.str.val = apc_string_pmemcpy(src->value.str.val, + src->value.str.len+1, + pool TSRMLS_CC)); + } + break; + + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + + CHECK(dst->value.ht = + my_copy_hashtable(NULL, + src->value.ht, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt)); + break; + + case IS_OBJECT: + + dst->type = IS_NULL; + if(ctxt->copy == APC_COPY_IN_USER) { + dst = my_serialize_object(dst, src, ctxt TSRMLS_CC); + } else if(ctxt->copy == APC_COPY_OUT_USER) { + dst = my_unserialize_object(dst, src, ctxt TSRMLS_CC); + } + break; + + default: + assert(0); + } + + return dst; +} +/* }}} */ + +#ifdef ZEND_ENGINE_2_4 +/* {{{ my_copy_znode */ +static void my_check_znode(zend_uchar op_type, apc_context_t* ctxt TSRMLS_DC) +{ + assert(op_type == IS_CONST || + op_type == IS_VAR || + op_type == IS_CV || + op_type == IS_TMP_VAR || + op_type == IS_UNUSED); +} +/* }}} */ + +/* {{{ my_copy_zend_op */ +static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + + my_check_znode(dst->result_type & ~EXT_TYPE_UNUSED, ctxt TSRMLS_CC); + my_check_znode(dst->op1_type, ctxt TSRMLS_CC); + my_check_znode(dst->op2_type, ctxt TSRMLS_CC); + + return dst; +} +/* }}} */ +#else +/* {{{ my_copy_znode */ +static znode* my_copy_znode(znode* dst, znode* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + +#ifdef IS_CV + assert(dst ->op_type == IS_CONST || + dst ->op_type == IS_VAR || + dst ->op_type == IS_CV || + dst ->op_type == IS_TMP_VAR || + dst ->op_type == IS_UNUSED); +#else + assert(dst ->op_type == IS_CONST || + dst ->op_type == IS_VAR || + dst ->op_type == IS_TMP_VAR || + dst ->op_type == IS_UNUSED); +#endif + + if (src->op_type == IS_CONST) { + if(!my_copy_zval(&dst->u.constant, &src->u.constant, ctxt TSRMLS_CC)) { + return NULL; + } + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_zend_op */ +static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + + CHECK(my_copy_znode(&dst->result, &src->result, ctxt TSRMLS_CC)); + CHECK(my_copy_znode(&dst->op1, &src->op1, ctxt TSRMLS_CC)); + CHECK(my_copy_znode(&dst->op2, &src->op2, ctxt TSRMLS_CC)); + + return dst; +} +/* }}} */ +#endif + +/* {{{ my_copy_function */ +static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(src != NULL); + + CHECK(dst = my_bitwise_copy_function(dst, src, ctxt TSRMLS_CC)); + + switch (src->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + /* shallow copy because op_array is internal */ + dst->op_array = src->op_array; + break; + + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + CHECK(apc_copy_op_array(&dst->op_array, + &src->op_array, + ctxt TSRMLS_CC)); + break; + + default: + assert(0); + } + /* + * op_array bitwise copying overwrites what ever you modified + * before apc_copy_op_array - which is why this code is outside + * my_bitwise_copy_function. + */ + + /* zend_do_inheritance will re-look this up, because the pointers + * in prototype are from a function table of another class. It just + * helps if that one is from EG(class_table). + */ + dst->common.prototype = NULL; + + /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you + * have to carry around a prototype. Thankfully zend_do_inheritance + * sets this properly as well + */ + dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); + + + return dst; +} +/* }}} */ + +/* {{{ my_copy_function_entry */ +static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, const zend_function_entry* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_function_entry*) apc_pool_alloc(ctxt->pool, sizeof(src[0]))); + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(src[0])); + + dst->fname = NULL; + dst->arg_info = NULL; + + if (src->fname) { + CHECK((dst->fname = apc_pstrdup(src->fname, ctxt->pool TSRMLS_CC))); + } + + if (src->arg_info) { + CHECK((dst->arg_info = my_copy_arg_info_array(NULL, + src->arg_info, + src->num_args, + ctxt TSRMLS_CC))); + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_property_info */ +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_context_t* ctxt TSRMLS_DC) +{ + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_property_info*) apc_pool_alloc(pool, sizeof(*src))); + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + dst->doc_comment = NULL; +#endif + + if (src->name) { + /* private members are stored inside property_info as a mangled + * string of the form: + * \0\0\0 + */ + CHECK((dst->name = apc_string_pmemcpy(src->name, src->name_length+1, pool TSRMLS_CC))); + } + +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + if (src->doc_comment) { + CHECK((dst->doc_comment = apc_pmemcpy(src->doc_comment, (src->doc_comment_len + 1), pool TSRMLS_CC))); + } +#endif + + return dst; +} +/* }}} */ + +/* {{{ my_copy_property_info_for_execution */ +static zend_property_info* my_copy_property_info_for_execution(zend_property_info* dst, zend_property_info* src, apc_context_t* ctxt TSRMLS_DC) +{ + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_property_info*) apc_pool_alloc(ctxt->pool, sizeof(*src))); + } + + /* We need only a shallow copy */ + memcpy(dst, src, sizeof(*src)); + + return dst; +} +/* }}} */ + +/* {{{ my_copy_arg_info_array */ +static zend_arg_info* my_copy_arg_info_array(zend_arg_info* dst, const zend_arg_info* src, uint num_args, apc_context_t* ctxt TSRMLS_DC) +{ + uint i = 0; + + + if (!dst) { + CHECK(dst = (zend_arg_info*) apc_pool_alloc(ctxt->pool, (sizeof(*src) * num_args))); + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)*num_args); + + for(i=0; i < num_args; i++) { + CHECK((my_copy_arg_info( &dst[i], &src[i], ctxt TSRMLS_CC))); + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_arg_info */ +static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, const zend_arg_info* src, apc_context_t* ctxt TSRMLS_DC) +{ + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_arg_info*) apc_pool_alloc(pool, sizeof(*src))); + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; + dst->class_name = NULL; + + if (src->name) { + CHECK((dst->name = apc_string_pmemcpy((char *) src->name, src->name_len+1, pool TSRMLS_CC))); + } + + if (src->class_name) { + CHECK((dst->class_name = apc_string_pmemcpy((char *) src->class_name, src->class_name_len+1, pool TSRMLS_CC))); + } + + return dst; +} +/* }}} */ + +/* {{{ apc_copy_class_entry */ +zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_context_t* ctxt TSRMLS_DC) +{ + return my_copy_class_entry(dst, src, ctxt TSRMLS_CC); +} + +/* {{{ my_copy_class_entry */ +static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_context_t* ctxt TSRMLS_DC) +{ + int i = 0; + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_class_entry*) apc_pool_alloc(pool, sizeof(*src))); + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; + memset(&dst->function_table, 0, sizeof(dst->function_table)); + ZEND_CE_DOC_COMMENT(dst) = NULL; + ZEND_CE_FILENAME(dst) = NULL; + memset(&dst->properties_info, 0, sizeof(dst->properties_info)); + memset(&dst->constants_table, 0, sizeof(dst->constants_table)); + + if (src->name) { + CHECK((dst->name = apc_pstrdup(src->name, pool TSRMLS_CC))); + } + + if(!(my_copy_hashtable_ex(&dst->function_table, + &src->function_table TSRMLS_CC, + (ht_copy_fun_t) my_copy_function, + 0, + ctxt, + (ht_check_copy_fun_t) my_check_copy_function, + src))) { + return NULL; + } + + /* the interfaces are populated at runtime using ADD_INTERFACE */ + dst->interfaces = NULL; + + /* the current count includes inherited interfaces as well, + the real dynamic ones are the first which are zero'd + out in zend_do_end_class_declaration */ + for(i = 0 ; (uint)i < src->num_interfaces ; i++) { + if(src->interfaces[i]) + { + dst->num_interfaces = i; + break; + } + } + + /* these will either be set inside my_fixup_hashtable or + * they will be copied out from parent inside zend_do_inheritance + */ + dst->parent = NULL; + dst->constructor = NULL; + dst->destructor = NULL; + dst->clone = NULL; + dst->__get = NULL; + dst->__set = NULL; + dst->__unset = NULL; + dst->__isset = NULL; + dst->__call = NULL; +#ifdef ZEND_ENGINE_2_2 + dst->__tostring = NULL; +#endif +#ifdef ZEND_ENGINE_2_3 + dst->__callstatic = NULL; +#endif + + /* unset function proxies */ + dst->serialize_func = NULL; + dst->unserialize_func = NULL; + + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst); + +#ifdef ZEND_ENGINE_2_4 + dst->default_properties_count = src->default_properties_count; + if (src->default_properties_count) { + CHECK(dst->default_properties_table = (zval**) apc_pool_alloc(pool, (sizeof(zval*) * src->default_properties_count))); + for (i = 0; i < src->default_properties_count; i++) { + if (src->default_properties_table[i]) { + my_copy_zval_ptr(&dst->default_properties_table[i], (const zval**)&src->default_properties_table[i], ctxt TSRMLS_CC); + } else { + dst->default_properties_table[i] = NULL; + } + } + } else { + dst->default_properties_table = NULL; + } +#else + memset(&dst->default_properties, 0, sizeof(dst->default_properties)); + CHECK((my_copy_hashtable_ex(&dst->default_properties, + &src->default_properties TSRMLS_CC, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt, + (ht_check_copy_fun_t) my_check_copy_default_property, + src))); +#endif + + CHECK((my_copy_hashtable_ex(&dst->properties_info, + &src->properties_info TSRMLS_CC, + (ht_copy_fun_t) my_copy_property_info, + 0, + ctxt, + (ht_check_copy_fun_t) my_check_copy_property_info, + src))); + +#ifdef ZEND_ENGINE_2_2 + /* php5.2 introduced a scope attribute for property info */ + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst); +#endif + +#ifdef ZEND_ENGINE_2_4 + dst->default_static_members_count = src->default_static_members_count; + + if (src->default_static_members_count) { + CHECK(dst->default_static_members_table = (zval**) apc_pool_alloc(pool, (sizeof(zval*) * src->default_static_members_count))); + for (i = 0; i < src->default_static_members_count; i++) { + if (src->default_static_members_table[i]) { + my_copy_zval_ptr(&dst->default_static_members_table[i], (const zval**)&src->default_static_members_table[i], ctxt TSRMLS_CC); + } else { + dst->default_static_members_table[i] = NULL; + } + } + } else { + dst->default_static_members_table = NULL; + } + dst->static_members_table = dst->default_static_members_table; +#else + memset(&dst->default_static_members, 0, sizeof(dst->default_static_members)); + dst->static_members = NULL; + CHECK(my_copy_hashtable_ex(&dst->default_static_members, + &src->default_static_members TSRMLS_CC, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt, + (ht_check_copy_fun_t) my_check_copy_static_member, + src, + &src->default_static_members)); + + if(src->static_members != &src->default_static_members) + { + CHECK((dst->static_members = my_copy_hashtable_ex(NULL, + src->static_members TSRMLS_CC, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt, + (ht_check_copy_fun_t) my_check_copy_static_member, + src, + src->static_members))); + } + else + { + dst->static_members = &dst->default_static_members; + } +#endif + + CHECK((my_copy_hashtable_ex(&dst->constants_table, + &src->constants_table TSRMLS_CC, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt, + (ht_check_copy_fun_t) my_check_copy_constant, + src))); + + if (src->type == ZEND_USER_CLASS && ZEND_CE_DOC_COMMENT(src)) { + CHECK(ZEND_CE_DOC_COMMENT(dst) = + apc_pmemcpy(ZEND_CE_DOC_COMMENT(src), (ZEND_CE_DOC_COMMENT_LEN(src) + 1), pool TSRMLS_CC)); + } + + if (src->type == ZEND_INTERNAL_CLASS && ZEND_CE_BUILTIN_FUNCTIONS(src)) { + int i, n; + + for (n = 0; src->type == ZEND_INTERNAL_CLASS && ZEND_CE_BUILTIN_FUNCTIONS(src)[n].fname != NULL; n++) {} + + CHECK((ZEND_CE_BUILTIN_FUNCTIONS(dst) = + (zend_function_entry*) apc_pool_alloc(pool, ((n + 1) * sizeof(zend_function_entry))))); + + for (i = 0; i < n; i++) { + CHECK(my_copy_function_entry((zend_function_entry*)(&ZEND_CE_BUILTIN_FUNCTIONS(dst)[i]), + &ZEND_CE_BUILTIN_FUNCTIONS(src)[i], + ctxt TSRMLS_CC)); + } + *(char**)&(ZEND_CE_BUILTIN_FUNCTIONS(dst)[n].fname) = NULL; + } + + if (src->type == ZEND_USER_CLASS && ZEND_CE_FILENAME(src)) { + CHECK((ZEND_CE_FILENAME(dst) = apc_pstrdup(ZEND_CE_FILENAME(src), pool TSRMLS_CC))); + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_hashtable_ex */ +static HashTable* my_copy_hashtable_ex(HashTable* dst, + HashTable* src TSRMLS_DC, + ht_copy_fun_t copy_fn, + int holds_ptrs, + apc_context_t* ctxt, + ht_check_copy_fun_t check_fn, + ...) +{ + Bucket* curr = NULL; + Bucket* prev = NULL; + Bucket* newp = NULL; + int first = 1; + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (HashTable*) apc_pool_alloc(pool, sizeof(src[0]))); + } + + memcpy(dst, src, sizeof(src[0])); + + /* allocate buckets for the new hashtable */ + CHECK((dst->arBuckets = apc_pool_alloc(pool, (dst->nTableSize * sizeof(Bucket*))))); + + memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*)); + dst->pInternalPointer = NULL; + dst->pListHead = NULL; + + for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) { + int n = curr->h % dst->nTableSize; + + if(check_fn) { + va_list args; + va_start(args, check_fn); + + /* Call the check_fn to see if the current bucket + * needs to be copied out + */ + if(!check_fn(curr, args)) { + dst->nNumOfElements--; + va_end(args); + continue; + } + + va_end(args); + } + + /* create a copy of the bucket 'curr' */ +#ifdef ZEND_ENGINE_2_4 + if (!curr->nKeyLength) { + CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket), pool TSRMLS_CC))); + } else if (IS_INTERNED(curr->arKey)) { + CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket), pool TSRMLS_CC))); + } else if (pool->type != APC_UNPOOL) { + char *arKey; + + arKey = apc_new_interned_string(curr->arKey, curr->nKeyLength TSRMLS_CC); + if (!arKey) { + CHECK((newp = (Bucket*) apc_pmemcpy(curr, (sizeof(Bucket) + curr->nKeyLength), pool TSRMLS_CC))); + newp->arKey = ((char*)newp) + sizeof(Bucket); + } else { + CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket), pool TSRMLS_CC))); + newp->arKey = arKey; + } + } else { + CHECK((newp = (Bucket*) apc_pmemcpy(curr, (sizeof(Bucket) + curr->nKeyLength), pool TSRMLS_CC))); + newp->arKey = ((char*)newp) + sizeof(Bucket); + } +#else + CHECK((newp = (Bucket*) apc_pmemcpy(curr, + (sizeof(Bucket) + curr->nKeyLength - 1), + pool TSRMLS_CC))); +#endif + + /* insert 'newp' into the linked list at its hashed index */ + if (dst->arBuckets[n]) { + newp->pNext = dst->arBuckets[n]; + newp->pLast = NULL; + newp->pNext->pLast = newp; + } + else { + newp->pNext = newp->pLast = NULL; + } + + dst->arBuckets[n] = newp; + + /* copy the bucket data using our 'copy_fn' callback function */ + CHECK((newp->pData = copy_fn(NULL, curr->pData, ctxt TSRMLS_CC))); + + if (holds_ptrs) { + memcpy(&newp->pDataPtr, newp->pData, sizeof(void*)); + } + else { + newp->pDataPtr = NULL; + } + + /* insert 'newp' into the table-thread linked list */ + newp->pListLast = prev; + newp->pListNext = NULL; + + if (prev) { + prev->pListNext = newp; + } + + if (first) { + dst->pListHead = newp; + first = 0; + } + + prev = newp; + } + + dst->pListTail = newp; + + return dst; +} +/* }}} */ + +/* {{{ my_copy_static_variables */ +static HashTable* my_copy_static_variables(zend_op_array* src, apc_context_t* ctxt TSRMLS_DC) +{ + if (src->static_variables == NULL) { + return NULL; + } + + return my_copy_hashtable(NULL, + src->static_variables, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt); +} +/* }}} */ + +/* {{{ apc_copy_zval */ +zval* apc_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC) +{ + apc_pool* pool = ctxt->pool; + int usegc = (ctxt->copy == APC_COPY_OUT_OPCODE) || (ctxt->copy == APC_COPY_OUT_USER); + + assert(src != NULL); + + if (!dst) { + if(usegc) { + ALLOC_ZVAL(dst); + CHECK(dst); + } else { + CHECK(dst = (zval*) apc_pool_alloc(pool, sizeof(zval))); + } + } + + CHECK(dst = my_copy_zval(dst, src, ctxt TSRMLS_CC)); + return dst; +} +/* }}} */ + +/* {{{ apc_fixup_op_array_jumps */ +static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src ) +{ + uint i; + + for (i=0; i < dst->last; ++i) { + zend_op *zo = &(dst->opcodes[i]); + /*convert opline number to jump address*/ + switch (zo->opcode) { +#ifdef ZEND_ENGINE_2_3 + case ZEND_GOTO: +#endif + case ZEND_JMP: + /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/ +#ifdef ZEND_ENGINE_2_4 + zo->op1.jmp_addr = dst->opcodes + (zo->op1.jmp_addr - src->opcodes); +#else + zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes); +#endif + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: +#ifdef ZEND_ENGINE_2_3 + case ZEND_JMP_SET: +#endif +#ifdef ZEND_ENGINE_2_4 + zo->op2.jmp_addr = dst->opcodes + (zo->op2.jmp_addr - src->opcodes); +#else + zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes); +#endif + break; + default: + break; + } + } +} +/* }}} */ + +/* {{{ apc_copy_op_array */ +zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_context_t* ctxt TSRMLS_DC) +{ + int i; + apc_fileinfo_t *fileinfo = NULL; + char canon_path[MAXPATHLEN]; + char *fullpath = NULL; + apc_opflags_t * flags = NULL; + apc_pool* pool = ctxt->pool; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_op_array*) apc_pool_alloc(pool, sizeof(src[0]))); + } + + if(APCG(apc_optimize_function)) { + APCG(apc_optimize_function)(src TSRMLS_CC); + } + + /* start with a bitwise copy of the array */ + memcpy(dst, src, sizeof(src[0])); + + dst->function_name = NULL; + dst->filename = NULL; + dst->refcount = NULL; + dst->opcodes = NULL; + dst->brk_cont_array = NULL; + dst->static_variables = NULL; + dst->try_catch_array = NULL; + dst->arg_info = NULL; + dst->doc_comment = NULL; +#ifdef ZEND_ENGINE_2_1 + dst->vars = NULL; +#endif + + /* copy the arg types array (if set) */ + if (src->arg_info) { + CHECK(dst->arg_info = my_copy_arg_info_array(NULL, + src->arg_info, + src->num_args, + ctxt TSRMLS_CC)); + } + + if (src->function_name) { + CHECK(dst->function_name = apc_pstrdup(src->function_name, pool TSRMLS_CC)); + } + if (src->filename) { + CHECK(dst->filename = apc_pstrdup(src->filename, pool TSRMLS_CC)); + } + + CHECK(dst->refcount = apc_pmemcpy(src->refcount, + sizeof(src->refcount[0]), + pool TSRMLS_CC)); + +#ifdef ZEND_ENGINE_2_4 + if (src->literals) { + zend_literal *p, *q, *end; + + q = src->literals; + p = dst->literals = (zend_literal*) apc_pool_alloc(pool, (sizeof(zend_literal) * src->last_literal)); + end = p + src->last_literal; + while (p < end) { + *p = *q; + my_copy_zval(&p->constant, &q->constant, ctxt TSRMLS_CC); + p++; + q++; + } + } +#endif + + /* deep-copy the opcodes */ + CHECK(dst->opcodes = (zend_op*) apc_pool_alloc(pool, (sizeof(zend_op) * src->last))); + + if(apc_reserved_offset != -1) { + /* Insanity alert: the void* pointer is cast into an apc_opflags_t + * struct. apc_zend_init() checks to ensure that it fits in a void* */ + flags = (apc_opflags_t*) & (dst->reserved[apc_reserved_offset]); + memset(flags, 0, sizeof(apc_opflags_t)); + /* assert(sizeof(apc_opflags_t) <= sizeof(dst->reserved)); */ + } + + for (i = 0; (uint) i < src->last; i++) { + zend_op *zo = &(src->opcodes[i]); + /* a lot of files are merely constant arrays with no jumps */ + switch (zo->opcode) { +#ifdef ZEND_ENGINE_2_3 + case ZEND_GOTO: +#endif + case ZEND_JMP: + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: +#ifdef ZEND_ENGINE_2_3 + case ZEND_JMP_SET: +#endif + if(flags != NULL) { + flags->has_jumps = 1; + } + break; + /* auto_globals_jit was not in php-4.3.* */ + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + case ZEND_FETCH_RW: + case ZEND_FETCH_UNSET: + if(PG(auto_globals_jit) && flags != NULL) + { + /* The fetch is only required if auto_globals_jit=1 */ +#ifdef ZEND_ENGINE_2_4 + if((zo->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL && + zo->op1_type == IS_CONST && + Z_TYPE_P(zo->op1.zv) == IS_STRING) { + if (Z_STRVAL_P(zo->op1.zv)[0] == '_') { +# define SET_IF_AUTOGLOBAL(member) \ + if(!strcmp(Z_STRVAL_P(zo->op1.zv), #member)) \ + flags->member = 1 /* no ';' here */ +#else + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && + zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_STRING) { + znode * varname = &zo->op1; + if (varname->u.constant.value.str.val[0] == '_') { +# define SET_IF_AUTOGLOBAL(member) \ + if(!strcmp(varname->u.constant.value.str.val, #member)) \ + flags->member = 1 /* no ';' here */ +#endif + SET_IF_AUTOGLOBAL(_GET); + else SET_IF_AUTOGLOBAL(_POST); + else SET_IF_AUTOGLOBAL(_COOKIE); + else SET_IF_AUTOGLOBAL(_SERVER); + else SET_IF_AUTOGLOBAL(_ENV); + else SET_IF_AUTOGLOBAL(_FILES); + else SET_IF_AUTOGLOBAL(_REQUEST); + else SET_IF_AUTOGLOBAL(_SESSION); +#ifdef ZEND_ENGINE_2_4 + else if(zend_is_auto_global( + Z_STRVAL_P(zo->op1.zv), + Z_STRLEN_P(zo->op1.zv) + TSRMLS_CC)) +#else + else if(zend_is_auto_global( + varname->u.constant.value.str.val, + varname->u.constant.value.str.len + TSRMLS_CC)) +#endif + { + flags->unknown_global = 1; + } +#ifdef ZEND_ENGINE_2_4 + } else SET_IF_AUTOGLOBAL(GLOBALS); +#else + } +#endif + } + } + break; + case ZEND_RECV_INIT: +#ifdef ZEND_ENGINE_2_4 + if(zo->op2_type == IS_CONST && + Z_TYPE_P(zo->op2.zv) == IS_CONSTANT_ARRAY) { +#else + if(zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY) { +#endif + if(flags != NULL) { + flags->deep_copy = 1; + } + } + break; + default: +#ifdef ZEND_ENGINE_2_4 + if((zo->op1_type == IS_CONST && + Z_TYPE_P(zo->op1.zv) == IS_CONSTANT_ARRAY) || + (zo->op2_type == IS_CONST && + Z_TYPE_P(zo->op2.zv) == IS_CONSTANT_ARRAY)) { +#else + if((zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_CONSTANT_ARRAY) || + (zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) { +#endif + if(flags != NULL) { + flags->deep_copy = 1; + } + } + break; + } + + if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, ctxt TSRMLS_CC))) { + return NULL; + } + +#ifdef ZEND_ENGINE_2_4 + if (zo->op1_type == IS_CONST) { + dst->opcodes[i].op1.literal = src->opcodes[i].op1.literal - src->literals + dst->literals; + } + if (zo->op2_type == IS_CONST) { + dst->opcodes[i].op2.literal = src->opcodes[i].op2.literal - src->literals + dst->literals; + } + if (zo->result_type == IS_CONST) { + dst->opcodes[i].result.literal = src->opcodes[i].result.literal - src->literals + dst->literals; + } +#endif + + /* This code breaks apc's rule#1 - cache what you compile */ + if((APCG(fpstat)==0) && APCG(canonicalize)) { + /* not pool allocated, because the pool allocations eat up shm space */ + fileinfo = (apc_fileinfo_t*) apc_php_malloc(sizeof(apc_fileinfo_t) TSRMLS_CC); +#ifdef ZEND_ENGINE_2_4 + if((zo->opcode == ZEND_INCLUDE_OR_EVAL) && + (zo->op1_type == IS_CONST && Z_TYPE_P(zo->op1.zv) == IS_STRING)) { + /* constant includes */ + if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(zo->op1.zv),Z_STRLEN_P(zo->op1.zv))) { + if (apc_search_paths(Z_STRVAL_P(zo->op1.zv), PG(include_path), fileinfo TSRMLS_CC) == 0) { +#else + if((zo->opcode == ZEND_INCLUDE_OR_EVAL) && + (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) { + /* constant includes */ + if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),Z_STRLEN_P(&zo->op1.u.constant))) { + if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), fileinfo TSRMLS_CC) == 0) { +#endif + if((fullpath = realpath(fileinfo->fullpath, canon_path))) { + /* everything has to go through a realpath() */ + zend_op *dzo = &(dst->opcodes[i]); +#ifdef ZEND_ENGINE_2_4 + dzo->op1.literal = (zend_literal*) apc_pool_alloc(pool, sizeof(zend_literal)); + Z_STRLEN_P(dzo->op1.zv) = strlen(fullpath); + Z_STRVAL_P(dzo->op1.zv) = apc_pstrdup(fullpath, pool TSRMLS_CC); + Z_SET_REFCOUNT_P(dzo->op1.zv, 2); + Z_SET_ISREF_P(dzo->op1.zv); + dzo->op1.literal->hash_value = zend_hash_func(Z_STRVAL_P(dzo->op1.zv), Z_STRLEN_P(dzo->op1.zv)+1); +#else + dzo->op1.u.constant.value.str.len = strlen(fullpath); + dzo->op1.u.constant.value.str.val = apc_pstrdup(fullpath, pool TSRMLS_CC); +#endif + } + } + } + } + apc_php_free(fileinfo TSRMLS_CC); + } + } + + if(flags == NULL || flags->has_jumps) { + apc_fixup_op_array_jumps(dst,src); + } + + /* copy the break-continue array */ + if (src->brk_cont_array) { + CHECK(dst->brk_cont_array = apc_pmemcpy(src->brk_cont_array, + sizeof(src->brk_cont_array[0]) * src->last_brk_cont, + pool TSRMLS_CC)); + } + + /* copy the table of static variables */ + if (src->static_variables) { + CHECK(dst->static_variables = my_copy_static_variables(src, ctxt TSRMLS_CC)); + } + + if (src->try_catch_array) { + CHECK(dst->try_catch_array = apc_pmemcpy(src->try_catch_array, + sizeof(src->try_catch_array[0]) * src->last_try_catch, + pool TSRMLS_CC)); + } + +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */ + if (src->vars) { + CHECK(dst->vars = apc_pmemcpy(src->vars, + sizeof(src->vars[0]) * src->last_var, + pool TSRMLS_CC)); + + for(i = 0; i < src->last_var; i++) dst->vars[i].name = NULL; + + for(i = 0; i < src->last_var; i++) { + CHECK(dst->vars[i].name = apc_string_pmemcpy(src->vars[i].name, + src->vars[i].name_len + 1, + pool TSRMLS_CC)); + } + } +#endif + + if (src->doc_comment) { + CHECK(dst->doc_comment + = apc_pmemcpy(src->doc_comment, (src->doc_comment_len + 1), pool TSRMLS_CC)); + } + + return dst; +} +/* }}} */ + + +/* {{{ apc_copy_new_functions */ +apc_function_t* apc_copy_new_functions(int old_count, apc_context_t* ctxt TSRMLS_DC) +{ + apc_function_t* array; + int new_count; /* number of new functions in table */ + int i; + apc_pool* pool = ctxt->pool; + + new_count = zend_hash_num_elements(CG(function_table)) - old_count; + assert(new_count >= 0); + + CHECK(array = + (apc_function_t*) + apc_pool_alloc(pool, (sizeof(apc_function_t) * (new_count + 1)))); + + if (new_count == 0) { + array[0].function = NULL; + return array; + } + + /* Skip the first `old_count` functions in the table */ + zend_hash_internal_pointer_reset(CG(function_table)); + for (i = 0; i < old_count; i++) { + zend_hash_move_forward(CG(function_table)); + } + + /* Add the next `new_count` functions to our array */ + for (i = 0; i < new_count; i++) { + char* key; + uint key_size; + zend_function* fun; + + zend_hash_get_current_key_ex(CG(function_table), + &key, + &key_size, + NULL, + 0, + NULL); + + zend_hash_get_current_data(CG(function_table), (void**) &fun); + + CHECK(array[i].name = apc_pmemcpy(key, (int) key_size, pool TSRMLS_CC)); + array[i].name_len = (int) key_size-1; + CHECK(array[i].function = my_copy_function(NULL, fun, ctxt TSRMLS_CC)); + zend_hash_move_forward(CG(function_table)); + } + + array[i].function = NULL; + return array; +} +/* }}} */ + +/* {{{ apc_copy_new_classes */ +apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_context_t *ctxt TSRMLS_DC) +{ + apc_class_t* array; + int new_count; /* number of new classes in table */ + int i; + apc_pool* pool = ctxt->pool; + + new_count = zend_hash_num_elements(CG(class_table)) - old_count; + assert(new_count >= 0); + + CHECK(array = + (apc_class_t*) + apc_pool_alloc(pool, (sizeof(apc_class_t) * (new_count + 1)))); + + if (new_count == 0) { + array[0].class_entry = NULL; + return array; + } + + /* Skip the first `old_count` classes in the table */ + zend_hash_internal_pointer_reset(CG(class_table)); + for (i = 0; i < old_count; i++) { + zend_hash_move_forward(CG(class_table)); + } + + /* Add the next `new_count` classes to our array */ + for (i = 0; i < new_count; i++) { + char* key; + uint key_size; + zend_class_entry* elem = NULL; + + array[i].class_entry = NULL; + + zend_hash_get_current_key_ex(CG(class_table), + &key, + &key_size, + NULL, + 0, + NULL); + + zend_hash_get_current_data(CG(class_table), (void**) &elem); + + + elem = *((zend_class_entry**)elem); + + CHECK(array[i].name = apc_pmemcpy(key, (int) key_size, pool TSRMLS_CC)); + array[i].name_len = (int) key_size-1; + CHECK(array[i].class_entry = my_copy_class_entry(NULL, elem, ctxt TSRMLS_CC)); + + /* + * If the class has a pointer to its parent class, save the parent + * name so that we can enable compile-time inheritance when we reload + * the child class; otherwise, set the parent name to null and scan + * the op_array to determine if this class inherits from some base + * class at execution-time. + */ + + if (elem->parent) { + CHECK(array[i].parent_name = apc_pstrdup(elem->parent->name, pool TSRMLS_CC)); + } + else { + array[i].parent_name = NULL; + } + + zend_hash_move_forward(CG(class_table)); + } + + array[i].class_entry = NULL; + return array; +} +/* }}} */ + +/* Used only by my_prepare_op_array_for_execution */ +#ifdef ZEND_ENGINE_2_4 +# define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION() \ + /* The fetch is only required if auto_globals_jit=1 */ \ + if((zo->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL && \ + zo->op1_type == IS_CONST && \ + Z_TYPE_P(zo->op1.zv) == IS_STRING && \ + Z_STRVAL_P(zo->op1.zv)[0] == '_') { \ + (void)zend_is_auto_global(Z_STRVAL_P(zo->op1.zv), \ + Z_STRLEN_P(zo->op1.zv) \ + TSRMLS_CC); \ + } +#else +# define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION() \ + /* The fetch is only required if auto_globals_jit=1 */ \ + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && \ + zo->op1.op_type == IS_CONST && \ + zo->op1.u.constant.type == IS_STRING && \ + zo->op1.u.constant.value.str.val[0] == '_') { \ + \ + znode* varname = &zo->op1; \ + (void)zend_is_auto_global(varname->u.constant.value.str.val, \ + varname->u.constant.value.str.len \ + TSRMLS_CC); \ + } +#endif + +/* {{{ my_prepare_op_array_for_execution */ +static int my_prepare_op_array_for_execution(zend_op_array* dst, zend_op_array* src, apc_context_t* ctxt TSRMLS_DC) +{ + /* combine my_fetch_global_vars and my_copy_data_exceptions. + * - Pre-fetch superglobals which would've been pre-fetched in parse phase. + * - If the opcode stream contain mutable data, ensure a copy. + * - Fixup array jumps in the same loop. + */ + int i=src->last; + zend_op *zo; + zend_op *dzo; + apc_opflags_t * flags = apc_reserved_offset != -1 ? + (apc_opflags_t*) & (src->reserved[apc_reserved_offset]) : NULL; + int needcopy = flags ? flags->deep_copy : 1; + /* auto_globals_jit was not in php4 */ + int do_prepare_fetch_global = PG(auto_globals_jit) && (flags == NULL || flags->unknown_global); + +#define FETCH_AUTOGLOBAL(member) do { \ + if(flags && flags->member == 1) { \ + zend_is_auto_global(#member,\ + (sizeof(#member) - 1)\ + TSRMLS_CC);\ + } \ +} while(0); + + FETCH_AUTOGLOBAL(_GET); + FETCH_AUTOGLOBAL(_POST); + FETCH_AUTOGLOBAL(_COOKIE); + FETCH_AUTOGLOBAL(_SERVER); + FETCH_AUTOGLOBAL(_ENV); + FETCH_AUTOGLOBAL(_FILES); + FETCH_AUTOGLOBAL(_REQUEST); + FETCH_AUTOGLOBAL(_SESSION); +#ifdef ZEND_ENGINE_2_4 + FETCH_AUTOGLOBAL(GLOBALS); +#endif + + if(needcopy) { + +#ifdef ZEND_ENGINE_2_4 + if (src->literals) { + zend_literal *p, *q, *end; + + q = src->literals; + p = dst->literals = (zend_literal*) apc_xmemcpy(src->literals, + sizeof(zend_literal) * src->last_literal, + apc_php_malloc TSRMLS_CC); + end = p + src->last_literal; + while (p < end) { + if (Z_TYPE(q->constant) == IS_CONSTANT_ARRAY) { + my_copy_zval(&p->constant, &q->constant, ctxt TSRMLS_CC); + } + p++; + q++; + } + } +#endif + + dst->opcodes = (zend_op*) apc_xmemcpy(src->opcodes, + sizeof(zend_op) * src->last, + apc_php_malloc TSRMLS_CC); + zo = src->opcodes; + dzo = dst->opcodes; + while(i > 0) { + +#ifdef ZEND_ENGINE_2_4 + if(zo->op1_type == IS_CONST) { + dzo->op1.literal = zo->op1.literal - src->literals + dst->literals; + } + if(zo->op2_type == IS_CONST) { + dzo->op2.literal = zo->op2.literal - src->literals + dst->literals; + } + if(zo->result_type == IS_CONST) { + dzo->result.literal = zo->result.literal - src->literals + dst->literals; + } +#else + if( ((zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) || + ((zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) { + + if(!(my_copy_zend_op(dzo, zo, ctxt TSRMLS_CC))) { + assert(0); /* emalloc failed or a bad constant array */ + } + } +#endif + + switch(zo->opcode) { +#ifdef ZEND_ENGINE_2_3 + case ZEND_GOTO: +#endif + case ZEND_JMP: +#ifdef ZEND_ENGINE_2_4 + dzo->op1.jmp_addr = dst->opcodes + + (zo->op1.jmp_addr - src->opcodes); +#else + dzo->op1.u.jmp_addr = dst->opcodes + + (zo->op1.u.jmp_addr - src->opcodes); +#endif + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: +#ifdef ZEND_ENGINE_2_3 + case ZEND_JMP_SET: +#endif +#ifdef ZEND_ENGINE_2_4 + dzo->op2.jmp_addr = dst->opcodes + + (zo->op2.jmp_addr - src->opcodes); +#else + dzo->op2.u.jmp_addr = dst->opcodes + + (zo->op2.u.jmp_addr - src->opcodes); +#endif + break; + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + if(do_prepare_fetch_global) + { + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION(); + } + break; + default: + break; + } + i--; + zo++; + dzo++; + } + } else { /* !needcopy */ + /* The fetch is only required if auto_globals_jit=1 */ + if(do_prepare_fetch_global) + { + zo = src->opcodes; + while(i > 0) { + + if(zo->opcode == ZEND_FETCH_R || + zo->opcode == ZEND_FETCH_W || + zo->opcode == ZEND_FETCH_IS || + zo->opcode == ZEND_FETCH_FUNC_ARG + ) { + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION(); + } + + i--; + zo++; + } + } + } + return 1; +} +/* }}} */ + +/* {{{ apc_copy_op_array_for_execution */ +zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src, apc_context_t* ctxt TSRMLS_DC) +{ + if(dst == NULL) { + dst = (zend_op_array*) emalloc(sizeof(src[0])); + } + memcpy(dst, src, sizeof(src[0])); + dst->static_variables = my_copy_static_variables(src, ctxt TSRMLS_CC); + + /* memory leak */ + dst->refcount = apc_pmemcpy(src->refcount, + sizeof(src->refcount[0]), + ctxt->pool TSRMLS_CC); + + my_prepare_op_array_for_execution(dst,src, ctxt TSRMLS_CC); + + return dst; +} +/* }}} */ + +/* {{{ apc_copy_function_for_execution */ +zend_function* apc_copy_function_for_execution(zend_function* src, apc_context_t* ctxt TSRMLS_DC) +{ + zend_function* dst; + + dst = (zend_function*) emalloc(sizeof(src[0])); + memcpy(dst, src, sizeof(src[0])); + apc_copy_op_array_for_execution(&(dst->op_array), &(src->op_array), ctxt TSRMLS_CC); + return dst; +} +/* }}} */ + +/* {{{ apc_copy_function_for_execution_ex */ +zend_function* apc_copy_function_for_execution_ex(void *dummy, zend_function* src, apc_context_t* ctxt TSRMLS_DC) +{ + if(src->type==ZEND_INTERNAL_FUNCTION || src->type==ZEND_OVERLOADED_FUNCTION) return src; + return apc_copy_function_for_execution(src, ctxt TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_copy_class_entry_for_execution */ +zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, apc_context_t* ctxt TSRMLS_DC) +{ +#ifdef ZEND_ENGINE_2_4 + int i; +#endif + zend_class_entry* dst = (zend_class_entry*) apc_pool_alloc(ctxt->pool, sizeof(src[0])); + memcpy(dst, src, sizeof(src[0])); + + if(src->num_interfaces) + { + /* These are slots to be populated later by ADD_INTERFACE insns */ + dst->interfaces = apc_php_malloc( + (sizeof(zend_class_entry*) * src->num_interfaces) TSRMLS_CC); + memset(dst->interfaces, 0, + sizeof(zend_class_entry*) * src->num_interfaces); + } + else + { + /* assert(dst->interfaces == NULL); */ + } + + /* Deep-copy the class properties, because they will be modified */ + +#ifdef ZEND_ENGINE_2_4 + dst->default_properties_count = src->default_properties_count; + if (src->default_properties_count) { + dst->default_properties_table = (zval**) apc_php_malloc((sizeof(zval*) * src->default_properties_count) TSRMLS_CC); + for (i = 0; i < src->default_properties_count; i++) { + if (src->default_properties_table[i]) { + my_copy_zval_ptr(&dst->default_properties_table[i], (const zval**)&src->default_properties_table[i], ctxt TSRMLS_CC); + } else { + dst->default_properties_table[i] = NULL; + } + } + } else { + dst->default_properties_table = NULL; + } +#else + my_copy_hashtable(&dst->default_properties, + &src->default_properties, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt); +#endif + + /* For derived classes, we must also copy the function hashtable (although + * we can merely bitwise copy the functions it contains) */ + + my_copy_hashtable(&dst->function_table, + &src->function_table, + (ht_copy_fun_t) apc_copy_function_for_execution_ex, + 0, + ctxt); + + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function_for_execution, src, dst); + + /* zend_do_inheritance merges properties_info. + * Need only shallow copying as it doesn't hold the pointers. + */ + my_copy_hashtable(&dst->properties_info, + &src->properties_info, + (ht_copy_fun_t) my_copy_property_info_for_execution, + 0, + ctxt); + +#ifdef ZEND_ENGINE_2_2 + /* php5.2 introduced a scope attribute for property info */ + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst); +#endif + + /* if inheritance results in a hash_del, it might result in + * a pefree() of the pointers here. Deep copying required. + */ + + my_copy_hashtable(&dst->constants_table, + &src->constants_table, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt); + +#ifdef ZEND_ENGINE_2_4 + dst->default_static_members_count = src->default_static_members_count; + if (src->default_static_members_count) { + dst->default_static_members_table = (zval**) apc_php_malloc((sizeof(zval*) * src->default_static_members_count) TSRMLS_CC); + for (i = 0; i < src->default_static_members_count; i++) { + if (src->default_static_members_table[i]) { + my_copy_zval_ptr(&dst->default_static_members_table[i], (const zval**)&src->default_static_members_table[i], ctxt TSRMLS_CC); + } else { + dst->default_static_members_table[i] = NULL; + } + } + } else { + dst->default_static_members_table = NULL; + } + dst->static_members_table = dst->default_static_members_table; +#else + my_copy_hashtable(&dst->default_static_members, + &src->default_static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt); + + if(src->static_members != &(src->default_static_members)) + { + dst->static_members = my_copy_hashtable(NULL, + src->static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + 1, + ctxt); + } + else + { + dst->static_members = &(dst->default_static_members); + } +#endif + + return dst; +} +/* }}} */ + +/* {{{ apc_free_class_entry_after_execution */ +void apc_free_class_entry_after_execution(zend_class_entry* src TSRMLS_DC) +{ + if(src->num_interfaces > 0 && src->interfaces) { + apc_php_free(src->interfaces TSRMLS_CC); + src->interfaces = NULL; + src->num_interfaces = 0; + } + /* my_destroy_hashtable() does not play nice with refcounts */ + +#ifdef ZEND_ENGINE_2_4 + if (src->default_static_members_table) { + int i; + + for (i = 0; i < src->default_static_members_count; i++) { + zval_ptr_dtor(&src->default_static_members_table[i]); + } + efree(src->default_static_members_table); + src->default_static_members_table = NULL; + } + src->static_members_table = NULL; + if (src->default_properties_table) { + int i; + + for (i = 0; i < src->default_properties_count; i++) { + if (src->default_properties_table[i]) { + zval_ptr_dtor(&src->default_properties_table[i]); + } + } + efree(src->default_properties_table); + src->default_properties_table = NULL; + } +#else + zend_hash_clean(&src->default_static_members); + if(src->static_members != &(src->default_static_members)) + { + zend_hash_destroy(src->static_members); + apc_php_free(src->static_members TSRMLS_CC); + src->static_members = NULL; + } + else + { + src->static_members = NULL; + } + + zend_hash_clean(&src->default_properties); +#endif + zend_hash_clean(&src->constants_table); + + /* TODO: more cleanup */ +} +/* }}} */ + +/* {{{ apc_file_halt_offset */ +long apc_file_halt_offset(const char *filename TSRMLS_DC) +{ + zend_constant *c; + char *name; + int len; + char haltoff[] = "__COMPILER_HALT_OFFSET__"; + long value = -1; + + zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, filename, strlen(filename), 0); + + if (zend_hash_find(EG(zend_constants), name, len+1, (void **) &c) == SUCCESS) { + value = Z_LVAL(c->value); + } + + pefree(name, 0); + + return value; +} +/* }}} */ + +/* {{{ apc_do_halt_compiler_register */ +void apc_do_halt_compiler_register(const char *filename, long halt_offset TSRMLS_DC) +{ + char *name; + char haltoff[] = "__COMPILER_HALT_OFFSET__"; + int len; + + if(halt_offset > 0) { + + zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, + filename, strlen(filename), 0); + + zend_register_long_constant(name, len+1, halt_offset, CONST_CS, 0 TSRMLS_CC); + + pefree(name, 0); + } +} +/* }}} */ + + + +/* {{{ my_fixup_function */ +static void my_fixup_function(Bucket *p, zend_class_entry *src, zend_class_entry *dst) +{ + zend_function* zf = p->pData; + + #define SET_IF_SAME_NAME(member) \ + do { \ + if(src->member && !strcmp(zf->common.function_name, src->member->common.function_name)) { \ + dst->member = zf; \ + } \ + } \ + while(0) + + if(zf->common.scope == src) + { + + /* Fixing up the default functions for objects here since + * we need to compare with the newly allocated functions + * + * caveat: a sub-class method can have the same name as the + * parent's constructor and create problems. + */ + + if(zf->common.fn_flags & ZEND_ACC_CTOR) dst->constructor = zf; + else if(zf->common.fn_flags & ZEND_ACC_DTOR) dst->destructor = zf; + else if(zf->common.fn_flags & ZEND_ACC_CLONE) dst->clone = zf; + else + { + SET_IF_SAME_NAME(__get); + SET_IF_SAME_NAME(__set); + SET_IF_SAME_NAME(__unset); + SET_IF_SAME_NAME(__isset); + SET_IF_SAME_NAME(__call); +#ifdef ZEND_ENGINE_2_2 + SET_IF_SAME_NAME(__tostring); +#endif +#ifdef ZEND_ENGINE_2_3 + SET_IF_SAME_NAME(__callstatic); +#endif + } + zf->common.scope = dst; + } + else + { + /* no other function should reach here */ + assert(0); + } + + #undef SET_IF_SAME_NAME +} +/* }}} */ + +#ifdef ZEND_ENGINE_2_2 +/* {{{ my_fixup_property_info */ +static void my_fixup_property_info(Bucket *p, zend_class_entry *src, zend_class_entry *dst) +{ + zend_property_info* property_info = (zend_property_info*)p->pData; + + if(property_info->ce == src) + { + property_info->ce = dst; + } + else + { + assert(0); /* should never happen */ + } +} +/* }}} */ +#endif + +/* {{{ my_fixup_hashtable */ +static void my_fixup_hashtable(HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst) +{ + Bucket *p; + uint i; + + for (i = 0; i < ht->nTableSize; i++) { + if(!ht->arBuckets) break; + p = ht->arBuckets[i]; + while (p != NULL) { + fixup(p, src, dst); + p = p->pNext; + } + } +} +/* }}} */ + + +/* {{{ my_check_copy_function */ +static int my_check_copy_function(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_function* zf = (zend_function*)p->pData; + + return (zf->common.scope == src); +} +/* }}} */ + +#ifndef ZEND_ENGINE_2_4 +/* {{{ my_check_copy_default_property */ +static int my_check_copy_default_property(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_class_entry* parent = src->parent; + zval ** child_prop = (zval**)p->pData; + zval ** parent_prop = NULL; + + if (parent && + zend_hash_quick_find(&parent->default_properties, p->arKey, + p->nKeyLength, p->h, (void **) &parent_prop)==SUCCESS) { + + if((parent_prop && child_prop) && (*parent_prop) == (*child_prop)) + { + return 0; + } + } + + /* possibly not in the parent */ + return 1; +} +/* }}} */ +#endif + +/* {{{ my_check_copy_property_info */ +static int my_check_copy_property_info(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_class_entry* parent = src->parent; + zend_property_info* child_info = (zend_property_info*)p->pData; + zend_property_info* parent_info = NULL; + +#ifdef ZEND_ENGINE_2_2 + /* so much easier */ + return (child_info->ce == src); +#endif + + if (parent && + zend_hash_quick_find(&parent->properties_info, p->arKey, p->nKeyLength, + p->h, (void **) &parent_info)==SUCCESS) { + if(parent_info->flags & ZEND_ACC_PRIVATE) + { + return 1; + } + if((parent_info->flags & ZEND_ACC_PPP_MASK) != + (child_info->flags & ZEND_ACC_PPP_MASK)) + { + /* TODO: figure out whether ACC_CHANGED is more appropriate + * here */ + return 1; + } + return 0; + } + + /* property doesn't exist in parent, copy into cached child */ + return 1; +} +/* }}} */ + +#ifndef ZEND_ENGINE_2_4 +/* {{{ my_check_copy_static_member */ +static int my_check_copy_static_member(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + HashTable * ht = va_arg(args, HashTable*); + zend_class_entry* parent = src->parent; + HashTable * parent_ht = NULL; + char * member_name; + char * class_name = NULL; + + zend_property_info *parent_info = NULL; + zend_property_info *child_info = NULL; + zval ** parent_prop = NULL; + zval ** child_prop = (zval**)(p->pData); + + if(!parent) { + return 1; + } + + /* these do not need free'ing */ +#ifdef ZEND_ENGINE_2_2 + zend_unmangle_property_name(p->arKey, p->nKeyLength-1, &class_name, &member_name); +#else + zend_unmangle_property_name(p->arKey, &class_name, &member_name); +#endif + + /* please refer do_inherit_property_access_check in zend_compile.c + * to understand why we lookup in properties_info. + */ + if((zend_hash_find(&parent->properties_info, member_name, + strlen(member_name)+1, (void**)&parent_info) == SUCCESS) + && + (zend_hash_find(&src->properties_info, member_name, + strlen(member_name)+1, (void**)&child_info) == SUCCESS)) + { + if(ht == &(src->default_static_members)) + { + parent_ht = &parent->default_static_members; + } + else + { + parent_ht = parent->static_members; + } + + if(zend_hash_quick_find(parent_ht, p->arKey, + p->nKeyLength, p->h, (void**)&parent_prop) == SUCCESS) + { + /* they point to the same zval */ + if(*parent_prop == *child_prop) + { + return 0; + } + } + } + + return 1; +} +/* }}} */ +#endif + +/* {{{ my_check_copy_constant */ +static int my_check_copy_constant(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_class_entry* parent = src->parent; + zval ** child_const = (zval**)p->pData; + zval ** parent_const = NULL; + + if (parent && + zend_hash_quick_find(&parent->constants_table, p->arKey, + p->nKeyLength, p->h, (void **) &parent_const)==SUCCESS) { + + if((parent_const && child_const) && (*parent_const) == (*child_const)) + { + return 0; + } + } + + /* possibly not in the parent */ + return 1; +} +/* }}} */ + +/* {{{ apc_register_optimizer(apc_optimize_function_t optimizer) + * register a optimizer callback function, returns the previous callback + */ +apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC) { + apc_optimize_function_t old_optimizer = APCG(apc_optimize_function); + APCG(apc_optimize_function) = optimizer; + return old_optimizer; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_compile.h @@ -0,0 +1,146 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_compile.h 303388 2010-09-15 09:50:28Z dmitry $ */ + +#ifndef APC_COMPILE_H +#define APC_COMPILE_H + +/* + * This module encapsulates most of the complexity involved in deep-copying + * the Zend compiler data structures. The routines are allocator-agnostic, so + * the same function can be used for copying to and from shared memory. + */ + +#include "apc.h" +#include "apc_php.h" +#include "apc_main.h" + +/* {{{ struct definition: apc_function_t */ +typedef struct apc_function_t apc_function_t; +struct apc_function_t { + char* name; /* the function name */ + int name_len; /* length of name */ + zend_function* function; /* the zend function data structure */ +}; +/* }}} */ + +/* {{{ struct definition: apc_class_t */ +typedef struct apc_class_t apc_class_t; +struct apc_class_t { + char* name; /* the class name */ + int name_len; /* length of name */ + char* parent_name; /* the parent class name */ + zend_class_entry* class_entry; /* the zend class data structure */ +}; +/* }}} */ + +/* {{{ struct definition: apc_opflags_t */ +typedef struct apc_opflags_t apc_opflags_t; +struct apc_opflags_t { + unsigned int has_jumps : 1; /* has jump offsets */ + unsigned int deep_copy : 1; /* needs deep copy */ + + /* autoglobal bits */ + unsigned int _POST : 1; + unsigned int _GET : 1; + unsigned int _COOKIE : 1; + unsigned int _SERVER : 1; + unsigned int _ENV : 1; + unsigned int _FILES : 1; + unsigned int _REQUEST : 1; + unsigned int _SESSION : 1; +#ifdef ZEND_ENGINE_2_4 + unsigned int GLOBALS : 1; +#endif + unsigned int unknown_global : 1; +}; +/* }}} */ + +/* + * These are the top-level copy functions. + */ + +extern zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_context_t* ctxt TSRMLS_DC); +extern zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_context_t* ctxt TSRMLS_DC); +extern apc_function_t* apc_copy_new_functions(int old_count, apc_context_t* ctxt TSRMLS_DC); +extern apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_context_t* ctxt TSRMLS_DC); +extern zval* apc_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC); + +#if 0 +/* + * Deallocation functions corresponding to the copy functions above. + */ + +extern void apc_free_op_array(zend_op_array* src, apc_context_t* ctxt); +extern void apc_free_functions(apc_function_t* src, apc_context_t* ctxt); +extern void apc_free_classes(apc_class_t* src, apc_context_t* ctxt); +extern void apc_free_zval(zval* src, apc_context_t* ctxt); +#endif + +/* + * These "copy-for-execution" functions must be called after retrieving an + * object from the shared cache. They do the minimal amount of work necessary + * to allow multiple processes to concurrently execute the same VM data + * structures. + */ + +extern zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src, apc_context_t* ctxt TSRMLS_DC); +extern zend_function* apc_copy_function_for_execution(zend_function* src, apc_context_t* ctxt TSRMLS_DC); +extern zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, apc_context_t* ctxt TSRMLS_DC); + +/* + * The "free-after-execution" function performs a cursory clean up of the class data + * This is required to minimize memory leak warnings and to ensure correct destructor + * ordering of some variables. + */ +extern void apc_free_class_entry_after_execution(zend_class_entry* src TSRMLS_DC); + +/* + * Optimization callback definition and registration function. + */ +typedef zend_op_array* (*apc_optimize_function_t) (zend_op_array* TSRMLS_DC); +extern apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC); + +/* + * To handle __COMPILER_HALT_OFFSET__ + */ +long apc_file_halt_offset(const char* filename TSRMLS_DC); +void apc_do_halt_compiler_register(const char *filename, long halt_offset TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_debug.c @@ -0,0 +1,70 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_debug.c 304107 2010-10-05 15:14:06Z kalle $ */ +#include "apc.h" +#include +#include "zend.h" +#include "zend_compile.h" + +#if defined(__DEBUG_APC__) + +/* keep track of vld_dump_oparray() signature */ +typedef void (*vld_dump_f) (zend_op_array * TSRMLS_DC); + +#endif + +void dump(zend_op_array *op_array TSRMLS_DC) +{ +#if defined(__DEBUG_APC__) + vld_dump_f dump_op_array; + DL_HANDLE handle = NULL; + +#ifdef PHP_WIN32 + handle = GetModuleHandle(NULL); + + if (!handle) { + apc_warning("unable to fetch current module handle." TSRMLS_CC); + } +#endif + + dump_op_array = (vld_dump_f) DL_FETCH_SYMBOL(handle, "vld_dump_oparray"); + +#ifdef PHP_WIN32 + DL_UNLOAD(handle); +#endif + + if(dump_op_array) { + dump_op_array(op_array TSRMLS_CC); + + return; + } + + apc_warning("vld is not installed or something even worse." TSRMLS_CC); +#endif +} --- /dev/null +++ b/ext/apc/apc_debug.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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. +*/ + +void dump(zend_op_array * TSRMLS_DC); --- /dev/null +++ b/ext/apc/apc_fcntl.c @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_fcntl.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc.h" + +#ifdef APC_FCNTL_LOCKS + +#include "apc_fcntl.h" +#include +#include + +int apc_fcntl_create(const char* pathname TSRMLS_DC) +{ + int fd; + if(pathname == NULL) { + char lock_path[] = "/tmp/.apc.XXXXXX"; + mktemp(lock_path); + fd = open(lock_path, O_RDWR|O_CREAT, 0666); + if(fd > 0 ) { + unlink(lock_path); + return fd; + } else { + apc_error("apc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:" TSRMLS_CC, lock_path); + return -1; + } + } + fd = open(pathname, O_RDWR|O_CREAT, 0666); + if(fd > 0 ) { + unlink(pathname); + return fd; + } + apc_error("apc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:" TSRMLS_CC, pathname); + return -1; +} + +void apc_fcntl_destroy(int fd) +{ + close(fd); +} + +static int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) +{ + int ret; + struct flock lock; + + lock.l_type = type; + lock.l_start = offset; + lock.l_whence = whence; + lock.l_len = len; + lock.l_pid = 0; + + do { ret = fcntl(fd, cmd, &lock) ; } + while(ret < 0 && errno == EINTR); + return(ret); +} + +void apc_fcntl_lock(int fd TSRMLS_DC) +{ + if(lock_reg(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) { + apc_error("apc_fcntl_lock failed:" TSRMLS_CC); + } +} + +void apc_fcntl_rdlock(int fd TSRMLS_DC) +{ + if(lock_reg(fd, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0) < 0) { + apc_error("apc_fcntl_rdlock failed:" TSRMLS_CC); + } +} + +zend_bool apc_fcntl_nonblocking_lock(int fd TSRMLS_DC) +{ + if(lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0) { + if(errno==EACCES||errno==EAGAIN) return 0; + else apc_error("apc_fcntl_lock failed:" TSRMLS_CC); + } + return 1; +} + +void apc_fcntl_unlock(int fd TSRMLS_DC) +{ + if(lock_reg(fd, F_SETLKW, F_UNLCK, 0, SEEK_SET, 0) < 0) { + apc_error("apc_fcntl_unlock failed:" TSRMLS_CC); + } +} + +#endif /* APC_FCNTL_LOCKS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_fcntl.h @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_fcntl.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_FCNTL_H +#define APC_FCNTL_H + + +extern int apc_fcntl_create(const char* pathname TSRMLS_DC); +extern void apc_fcntl_destroy(int fd); +extern void apc_fcntl_lock(int fd TSRMLS_DC); +extern void apc_fcntl_rdlock(int fd TSRMLS_DC); +extern void apc_fcntl_unlock(int fd TSRMLS_DC); +extern unsigned char apc_fcntl_nonblocking_lock(int fd TSRMLS_DC); +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_fcntl_win32.c @@ -0,0 +1,117 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: George Schlossnagle | + | Edin Kadribasic | + +----------------------------------------------------------------------+ + + 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: apc_fcntl_win32.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc.h" +#include "apc_fcntl.h" +#include +#include +#include +#include +#include +#include + +int apc_fcntl_create(const char* pathname TSRMLS_DC) +{ + char *lock_file = emalloc(MAXPATHLEN); + HANDLE fd; + DWORD tmplen; + static int i=0; + + tmplen = GetTempPath(MAXPATHLEN, lock_file); + if (!tmplen) { + efree(lock_file); + return -1; + } + + snprintf(lock_file + tmplen, MAXPATHLEN - tmplen - 1, "apc.lock.%d", i++); + + fd = CreateFile(lock_file, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + + if (fd == INVALID_HANDLE_VALUE) { + apc_error("apc_fcntl_create: could not open %s" TSRMLS_CC, lock_file); + efree(lock_file); + return -1; + } + + efree(lock_file); + return (int)fd; +} + +void apc_fcntl_destroy(int fd) +{ + CloseHandle((HANDLE)fd); +} + +void apc_fcntl_lock(int fd TSRMLS_DC) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!LockFileEx((HANDLE)fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset)) { + apc_error("apc_fcntl_lock failed errno:%d" TSRMLS_CC, GetLastError()); + } +} + +void apc_fcntl_rdlock(int fd TSRMLS_DC) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!LockFileEx((HANDLE)fd, 0, 0, 1, 0, &offset)) { + apc_error("apc_fcntl_rdlock failed errno:%d" TSRMLS_CC, GetLastError()); + } +} + +void apc_fcntl_unlock(int fd TSRMLS_DC) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!UnlockFileEx((HANDLE)fd, 0, 1, 0, &offset)) { + DWORD error_code = GetLastError(); + /* Ignore already unlocked error */ + if (error_code != ERROR_NOT_LOCKED) { + apc_error("apc_fcntl_unlock failed errno:%d" TSRMLS_CC, error_code); + } + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_globals.h @@ -0,0 +1,148 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_globals.h 301232 2010-07-13 12:23:35Z gopalv $ */ + +#ifndef APC_GLOBALS_H +#define APC_GLOBALS_H + +#include "apc_cache.h" +#include "apc_stack.h" +#include "apc_php.h" + +/* {{{ struct apc_rfc1867_data */ + +typedef struct _apc_rfc1867_data apc_rfc1867_data; + +struct _apc_rfc1867_data { + char tracking_key[64]; + int key_length; + size_t content_length; + char filename[128]; + char name[64]; + char *temp_filename; + int cancel_upload; + double start_time; + size_t bytes_processed; + size_t prev_bytes_processed; + int update_freq; + double rate; + int started; +}; +/* }}} */ + +ZEND_BEGIN_MODULE_GLOBALS(apc) + /* configuration parameters */ + zend_bool enabled; /* if true, apc is enabled (defaults to true) */ + long shm_segments; /* number of shared memory segments to use */ + long shm_size; /* size of each shared memory segment (in MB) */ + long num_files_hint; /* parameter to apc_cache_create */ + long user_entries_hint; + long gc_ttl; /* parameter to apc_cache_create */ + long ttl; /* parameter to apc_cache_create */ + long user_ttl; +#if APC_MMAP + char *mmap_file_mask; /* mktemp-style file-mask to pass to mmap */ +#endif + char** filters; /* array of regex filters that prevent caching */ + void* compiled_filters; /* compiled regex filters */ + + /* module variables */ + zend_bool initialized; /* true if module was initialized */ + apc_stack_t* cache_stack; /* the stack of cached executable code */ + zend_bool cache_by_default; /* true if files should be cached unless filtered out */ + /* false if files should only be cached if filtered in */ + long file_update_protection; /* Age in seconds before a file is eligible to be cached - 0 to disable */ + zend_bool enable_cli; /* Flag to override turning APC off for CLI */ + long max_file_size; /* Maximum size of file, in bytes that APC will be allowed to cache */ + zend_bool fpstat; /* true if fullpath includes should be stat'ed */ + zend_bool canonicalize; /* true if relative paths should be canonicalized in no-stat mode */ + zend_bool stat_ctime; /* true if ctime in addition to mtime should be checked */ + zend_bool write_lock; /* true for a global write lock */ + zend_bool slam_defense; /* true for user cache slam defense */ + zend_bool report_autofilter; /* true for auto-filter warnings */ + zend_bool include_once; /* Override the ZEND_INCLUDE_OR_EVAL opcode handler to avoid pointless fopen()s [still experimental] */ + apc_optimize_function_t apc_optimize_function; /* optimizer function callback */ +#ifdef MULTIPART_EVENT_FORMDATA + zend_bool rfc1867; /* Flag to enable rfc1867 handler */ + char* rfc1867_prefix; /* Key prefix */ + char* rfc1867_name; /* Name of hidden field to activate upload progress/key suffix */ + double rfc1867_freq; /* Update frequency as percentage or bytes */ + long rfc1867_ttl; /* TTL for rfc1867 entries */ + apc_rfc1867_data rfc1867_data;/* Per-request data */ +#endif + HashTable copied_zvals; /* my_copy recursion detection list */ + zend_bool force_file_update; /* force files to be updated during apc_compile_file */ + char canon_path[MAXPATHLEN]; /* canonical path for key data */ +#ifdef APC_FILEHITS + zval *filehits; /* Files that came from the cache for this request */ +#endif + zend_bool coredump_unmap; /* Trap signals that coredump and unmap shared memory */ + apc_cache_t *current_cache; /* current cache being modified/read */ + char *preload_path; + zend_bool file_md5; /* record md5 hash of files */ + void *apc_bd_alloc_ptr; /* bindump alloc() ptr */ + void *apc_bd_alloc_ubptr; /* bindump alloc() upper bound ptr */ + HashTable apc_bd_alloc_list; /* bindump alloc() ptr list */ + zend_bool use_request_time; /* use the SAPI request start time for TTL */ + zend_bool lazy_functions; /* enable/disable lazy function loading */ + HashTable *lazy_function_table; /* lazy function entry table */ + zend_bool lazy_classes; /* enable/disable lazy class loading */ + HashTable *lazy_class_table; /* lazy class entry table */ +#ifdef ZEND_ENGINE_2_4 + long shm_strings_buffer; +#endif +ZEND_END_MODULE_GLOBALS(apc) + +/* (the following declaration is defined in php_apc.c) */ +ZEND_EXTERN_MODULE_GLOBALS(apc) + +#ifdef ZTS +# define APCG(v) TSRMG(apc_globals_id, zend_apc_globals *, v) +#else +# define APCG(v) (apc_globals.v) +#endif + +/* True globals */ +extern apc_cache_t* apc_cache; /* the global compiler cache */ +extern apc_cache_t* apc_user_cache; /* the global user content cache */ +extern void* apc_compiled_filters; /* compiled filters */ + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc.h @@ -0,0 +1,124 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc.h 304310 2010-10-11 12:19:24Z gopalv $ */ + +#ifndef APC_H +#define APC_H + +/* + * This module defines utilities and helper functions used elsewhere in APC. + */ + +/* Commonly needed C library headers. */ +#include +#include +#include +#include +#include +#include +#include + +/* UNIX headers (needed for struct stat) */ +#include +#include +#ifndef PHP_WIN32 +#include +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#include "main/php_streams.h" + +/* typedefs for extensible memory allocators */ +typedef void* (*apc_malloc_t)(size_t TSRMLS_DC); +typedef void (*apc_free_t) (void * TSRMLS_DC); + +/* wrappers for memory allocation routines */ +extern void* apc_emalloc(size_t n TSRMLS_DC); +extern void* apc_erealloc(void* p, size_t n TSRMLS_DC); +extern void apc_efree(void* p TSRMLS_DC); +extern char* apc_estrdup(const char* s TSRMLS_DC); +extern void* apc_xstrdup(const char* s, apc_malloc_t f TSRMLS_DC); +extern void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f TSRMLS_DC); + +/* console display functions */ +extern void apc_error(const char *format TSRMLS_DC, ...); +extern void apc_warning(const char *format TSRMLS_DC, ...); +extern void apc_notice(const char *format TSRMLS_DC, ...); +extern void apc_debug(const char *format TSRMLS_DC, ...); + +/* string and text manipulation */ +extern char* apc_append(const char* s, const char* t TSRMLS_DC); +extern char* apc_substr(const char* s, int start, int length TSRMLS_DC); +extern char** apc_tokenize(const char* s, char delim TSRMLS_DC); + +/* filesystem functions */ + +typedef struct apc_fileinfo_t +{ + char *fullpath; + char path_buf[MAXPATHLEN]; + php_stream_statbuf st_buf; +} apc_fileinfo_t; + +extern int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo TSRMLS_DC); + +/* regular expression wrapper functions */ +extern void* apc_regex_compile_array(char* patterns[] TSRMLS_DC); +extern void apc_regex_destroy_array(void* p TSRMLS_DC); +extern int apc_regex_match_array(void* p, const char* input); + +/* apc_crc32: returns the CRC-32 checksum of the first len bytes in buf */ +extern unsigned int apc_crc32(const char* buf, int len); + +/* apc_flip_hash flips keys and values for faster searching */ +extern HashTable* apc_flip_hash(HashTable *hash); + +#define APC_NEGATIVE_MATCH 1 +#define APC_POSITIVE_MATCH 2 + +#define apc_time() \ + (APCG(use_request_time) ? sapi_get_request_time(TSRMLS_C) : time(0)); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_iterator.c @@ -0,0 +1,710 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_iterator.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "php_apc.h" +#include "apc_iterator.h" +#include "apc_cache.h" +#include "apc_zend.h" + +#include "ext/standard/md5.h" + +#include "zend_interfaces.h" + +zend_class_entry *apc_iterator_ce; +zend_object_handlers apc_iterator_object_handlers; + + +/* {{{ apc_iterator_item */ +static apc_iterator_item_t* apc_iterator_item_ctor(apc_iterator_t *iterator, slot_t **slot_pp TSRMLS_DC) { + zval *zvalue; + char md5str[33]; + slot_t *slot = *slot_pp; + apc_context_t ctxt = {0, }; + apc_iterator_item_t *item = ecalloc(1, sizeof(apc_iterator_item_t)); + + if (slot->key.type == APC_CACHE_KEY_FILE) { + /* keys should be unique and with stat=1 we could have multiple files with the same name, so use ' ' instead */ +#ifdef PHP_WIN32 + item->key_len = spprintf(&item->key, 0, "%I64d %I64d", slot->key.data.file.device, slot->key.data.file.inode); +#else + item->key_len = spprintf(&item->key, 0, "%ld %ld", (ulong)slot->key.data.file.device, (ulong)slot->key.data.file.inode); +#endif + item->filename_key = estrdup(slot->value->data.file.filename); + } else if (slot->key.type == APC_CACHE_KEY_USER) { + item->key = estrndup((char*)slot->key.data.user.identifier, slot->key.data.user.identifier_len); + item->key_len = slot->key.data.user.identifier_len; + item->filename_key = item->key; + } else if (slot->key.type == APC_CACHE_KEY_FPFILE) { + item->key = estrndup((char*)slot->key.data.fpfile.fullpath, slot->key.data.fpfile.fullpath_len); + item->key_len = slot->key.data.fpfile.fullpath_len; + item->filename_key = item->key; + } else { + apc_error("Internal error, invalid entry type." TSRMLS_CC); + } + + ALLOC_INIT_ZVAL(item->value); + array_init(item->value); + + if (APC_ITER_TYPE & iterator->format) { + if(slot->value->type == APC_CACHE_ENTRY_FILE) { + add_assoc_string(item->value, "type", "file", 1); + } else if(slot->value->type == APC_CACHE_ENTRY_USER) { + add_assoc_string(item->value, "type", "user", 1); + } + } + if (APC_ITER_FILENAME & iterator->format) { + if(slot->value->type == APC_CACHE_ENTRY_FILE) { + if (slot->key.type == APC_CACHE_KEY_FILE) { + add_assoc_string(item->value, "filename", slot->value->data.file.filename, 1); + } else { /* APC_CACHE_FPFILE */ + add_assoc_string(item->value, "filename", (char*)slot->key.data.fpfile.fullpath, 1); + } + } + } + if (APC_ITER_DEVICE & iterator->format) { + if(slot->key.type == APC_CACHE_KEY_FILE) { +#ifdef PHP_WIN32 + char buf[20]; + sprintf(buf, "%I64d", slot->key.data.file.device); + add_assoc_string(item->value, "device", buf, 1); +#else + add_assoc_long(item->value, "device", slot->key.data.file.device); +#endif + } + } + if (APC_ITER_INODE & iterator->format) { + if(slot->key.type == APC_CACHE_KEY_FILE) { +#ifdef PHP_WIN32 + char buf[20]; + sprintf(buf, "%I64d", slot->key.data.file.device); + add_assoc_string(item->value, "device", buf, 1); +#else + add_assoc_long(item->value, "inode", slot->key.data.file.inode); +#endif + } + } + if (APC_ITER_KEY & iterator->format) { + add_assoc_stringl(item->value, "key", item->key, (item->key_len - 1), 1); + } + if (APC_ITER_VALUE & iterator->format) { + if(slot->value->type == APC_CACHE_ENTRY_USER) { + + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_php_malloc, apc_php_free, NULL, NULL TSRMLS_CC); + ctxt.copy = APC_COPY_OUT_USER; + + MAKE_STD_ZVAL(zvalue); + apc_cache_fetch_zval(zvalue, slot->value->data.user.val, &ctxt TSRMLS_CC); + apc_pool_destroy(ctxt.pool TSRMLS_CC); + add_assoc_zval(item->value, "value", zvalue); + } + } + if (APC_ITER_MD5 & iterator->format) { + if(slot->value->type == APC_CACHE_ENTRY_FILE) { + if(slot->key.md5) { + make_digest(md5str, slot->key.md5); + add_assoc_string(item->value, "md5", md5str, 1); + } + } + } + if (APC_ITER_NUM_HITS & iterator->format) { + add_assoc_long(item->value, "num_hits", slot->num_hits); + } + if (APC_ITER_MTIME & iterator->format) { + add_assoc_long(item->value, "mtime", slot->key.mtime); + } + if (APC_ITER_CTIME & iterator->format) { + add_assoc_long(item->value, "creation_time", slot->creation_time); + } + if (APC_ITER_DTIME & iterator->format) { + add_assoc_long(item->value, "deletion_time", slot->deletion_time); + } + if (APC_ITER_ATIME & iterator->format) { + add_assoc_long(item->value, "access_time", slot->access_time); + } + if (APC_ITER_REFCOUNT & iterator->format) { + add_assoc_long(item->value, "ref_count", slot->value->ref_count); + } + if (APC_ITER_MEM_SIZE & iterator->format) { + add_assoc_long(item->value, "mem_size", slot->value->mem_size); + } + if (APC_ITER_TTL & iterator->format) { + if(slot->value->type == APC_CACHE_ENTRY_USER) { + add_assoc_long(item->value, "ttl", slot->value->data.user.ttl); + } + } + + return item; +} +/* }}} */ + +/* {{{ apc_iterator_clone */ +static zend_object_value apc_iterator_clone(zval *zobject TSRMLS_DC) { + zend_object_value value = {0}; + apc_error("APCIterator object cannot be cloned." TSRMLS_CC); + return value; +} +/* }}} */ + +/* {{{ apc_iterator_item_dtor */ +static void apc_iterator_item_dtor(apc_iterator_item_t *item) { + if (item->filename_key && item->filename_key != item->key) { + efree(item->filename_key); + } + if (item->key) { + efree(item->key); + } + if (item->value) { + zval_ptr_dtor(&item->value); + } + efree(item); +} +/* }}} */ + +/* {{{ apc_iterator_destroy */ +static void apc_iterator_destroy(void *object, zend_object_handle handle TSRMLS_DC) { + apc_iterator_t *iterator = (apc_iterator_t*)object; + + if (iterator->initialized == 0) { + return; + } + + while (apc_stack_size(iterator->stack) > 0) { + apc_iterator_item_dtor(apc_stack_pop(iterator->stack)); + } + if (iterator->regex) { + efree(iterator->regex); + } + if (iterator->search_hash) { + zend_hash_destroy(iterator->search_hash); + efree(iterator->search_hash); + } + iterator->initialized = 0; + +} +/* }}} */ + +/* {{{ acp_iterator_free */ +static void apc_iterator_free(void *object TSRMLS_DC) { + zend_object_std_dtor(object TSRMLS_CC); + efree(object); +} +/* }}} */ + +/* {{{ apc_iterator_create */ +static zend_object_value apc_iterator_create(zend_class_entry *ce TSRMLS_DC) { + zend_object_value retval; + apc_iterator_t *iterator; + + iterator = emalloc(sizeof(apc_iterator_t)); + iterator->obj.ce = ce; + ALLOC_HASHTABLE(iterator->obj.properties); + zend_hash_init(iterator->obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + iterator->obj.guards = NULL; + iterator->initialized = 0; + retval.handle = zend_objects_store_put(iterator, apc_iterator_destroy, apc_iterator_free, NULL TSRMLS_CC); + retval.handlers = &apc_iterator_object_handlers; + + return retval; +} +/* }}} */ + +/* {{{ apc_iterator_search_match + * Verify if the key matches oru search parameters + */ +static int apc_iterator_search_match(apc_iterator_t *iterator, slot_t **slot) { + char *key; + int key_len; + char *fname_key = NULL; + int fname_key_len; + int rval = 1; + + if ((*slot)->key.type == APC_CACHE_KEY_FILE) { + key = estrdup((*slot)->value->data.file.filename); + key_len = strlen(key); + fname_key_len = spprintf(&fname_key, 0, "%ld %ld", (*slot)->key.data.file.device, (*slot)->key.data.file.inode); + } else if ((*slot)->key.type == APC_CACHE_KEY_USER) { + key = (char*)(*slot)->key.data.user.identifier; + key_len = (*slot)->key.data.user.identifier_len; + } else if ((*slot)->key.type == APC_CACHE_KEY_FPFILE) { + key = (char*)(*slot)->key.data.fpfile.fullpath; + key_len = (*slot)->key.data.fpfile.fullpath_len; + } + +#ifdef ITERATOR_PCRE + if (iterator->regex) { + rval = (pcre_exec(iterator->re, NULL, key, strlen(key), 0, 0, NULL, 0) >= 0); + } +#endif + + if (iterator->search_hash) { + rval = zend_hash_exists(iterator->search_hash, key, key_len); + if (!rval && fname_key) { + rval = zend_hash_exists(iterator->search_hash, fname_key, fname_key_len+1); + } + } + + return rval; +} +/* }}} */ + +/* {{{ apc_iterator_fetch_active */ +static int apc_iterator_fetch_active(apc_iterator_t *iterator TSRMLS_DC) { + int count=0; + slot_t **slot; + apc_iterator_item_t *item; + + while (apc_stack_size(iterator->stack) > 0) { + apc_iterator_item_dtor(apc_stack_pop(iterator->stack)); + } + + CACHE_LOCK(iterator->cache); + while(count <= iterator->chunk_size && iterator->slot_idx < iterator->cache->num_slots) { + slot = &iterator->cache->slots[iterator->slot_idx]; + while(*slot) { + if (apc_iterator_search_match(iterator, slot)) { + count++; + item = apc_iterator_item_ctor(iterator, slot TSRMLS_CC); + if (item) { + apc_stack_push(iterator->stack, item TSRMLS_CC); + } + } + slot = &(*slot)->next; + } + iterator->slot_idx++; + } + CACHE_UNLOCK(iterator->cache); + iterator->stack_idx = 0; + return count; +} +/* }}} */ + +/* {{{ apc_iterator_fetch_deleted */ +static int apc_iterator_fetch_deleted(apc_iterator_t *iterator TSRMLS_DC) { + int count=0; + slot_t **slot; + apc_iterator_item_t *item; + + CACHE_LOCK(iterator->cache); + slot = &iterator->cache->header->deleted_list; + while ((*slot) && count <= iterator->slot_idx) { + count++; + slot = &(*slot)->next; + } + count = 0; + while ((*slot) && count < iterator->chunk_size) { + if (apc_iterator_search_match(iterator, slot)) { + count++; + item = apc_iterator_item_ctor(iterator, slot TSRMLS_CC); + if (item) { + apc_stack_push(iterator->stack, item TSRMLS_CC); + } + } + slot = &(*slot)->next; + } + CACHE_UNLOCK(iterator->cache); + iterator->slot_idx += count; + iterator->stack_idx = 0; + return count; +} +/* }}} */ + +/* {{{ apc_iterator_totals */ +static void apc_iterator_totals(apc_iterator_t *iterator TSRMLS_DC) { + slot_t **slot; + int i; + + CACHE_LOCK(iterator->cache); + for (i=0; i < iterator->cache->num_slots; i++) { + slot = &iterator->cache->slots[i]; + while((*slot)) { + if (apc_iterator_search_match(iterator, slot)) { + iterator->size += (*slot)->value->mem_size; + iterator->hits += (*slot)->num_hits; + iterator->count++; + } + slot = &(*slot)->next; + } + } + CACHE_UNLOCK(iterator->cache); + iterator->totals_flag = 1; +} +/* }}} */ + +/* {{{ proto object APCIterator::__costruct(string cache [, mixed search [, long format [, long chunk_size [, long list ]]]]) */ +PHP_METHOD(apc_iterator, __construct) { + zval *object = getThis(); + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(object TSRMLS_CC); + char *cachetype; + int cachetype_len; + long format = APC_ITER_ALL; + long chunk_size=0; + zval *search = NULL; + long list = APC_LIST_ACTIVE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zlll", &cachetype, &cachetype_len, &search, &format, &chunk_size, &list) == FAILURE) { + return; + } + + if (!APCG(enabled)) { + apc_error("APC must be enabled to use APCIterator." TSRMLS_CC); + } + + if (chunk_size < 0) { + apc_error("APCIterator chunk size must be 0 or greater." TSRMLS_CC); + return; + } + + if (format > APC_ITER_ALL) { + apc_error("APCIterator format is invalid." TSRMLS_CC); + return; + } + + if (list == APC_LIST_ACTIVE) { + iterator->fetch = apc_iterator_fetch_active; + } else if (list == APC_LIST_DELETED) { + iterator->fetch = apc_iterator_fetch_deleted; + } else { + apc_warning("APCIterator invalid list type." TSRMLS_CC); + return; + } + + if(!strcasecmp(cachetype,"user")) { + iterator->cache = apc_user_cache; + } else { + iterator->cache = apc_cache; + } + + iterator->slot_idx = 0; + iterator->stack_idx = 0; + iterator->key_idx = 0; + iterator->chunk_size = chunk_size == 0 ? APC_DEFAULT_CHUNK_SIZE : chunk_size; + iterator->stack = apc_stack_create(chunk_size TSRMLS_CC); + iterator->format = format; + iterator->totals_flag = 0; + iterator->count = 0; + iterator->size = 0; + iterator->hits = 0; + iterator->regex = NULL; + iterator->regex_len = 0; + iterator->search_hash = NULL; + if (search && Z_TYPE_P(search) == IS_STRING && Z_STRLEN_P(search)) { +#ifdef ITERATOR_PCRE + iterator->regex = estrndup(Z_STRVAL_P(search), Z_STRLEN_P(search)); + iterator->regex_len = Z_STRLEN_P(search); + iterator->re = pcre_get_compiled_regex(Z_STRVAL_P(search), NULL, NULL TSRMLS_CC); + + if(!iterator->re) { + apc_error("Could not compile regular expression: %s" TSRMLS_CC, Z_STRVAL_P(search)); + } +#else + apc_error("Regular expressions support is not enabled, please enable PCRE for APCIterator regex support" TSRMLS_CC); +#endif + } else if (search && Z_TYPE_P(search) == IS_ARRAY) { + Z_ADDREF_P(search); + iterator->search_hash = apc_flip_hash(Z_ARRVAL_P(search)); + } + iterator->initialized = 1; +} +/* }}} */ + +/* {{{ proto APCIterator::rewind() */ +PHP_METHOD(apc_iterator, rewind) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + iterator->slot_idx = 0; + iterator->stack_idx = 0; + iterator->key_idx = 0; + iterator->fetch(iterator TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto boolean APCIterator::valid() */ +PHP_METHOD(apc_iterator, valid) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + if (apc_stack_size(iterator->stack) == iterator->stack_idx) { + iterator->fetch(iterator TSRMLS_CC); + } + + RETURN_BOOL(apc_stack_size(iterator->stack) == 0 ? 0 : 1); +} +/* }}} */ + +/* {{{ proto mixed APCIterator::current() */ +PHP_METHOD(apc_iterator, current) { + apc_iterator_item_t *item; + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + if (apc_stack_size(iterator->stack) == iterator->stack_idx) { + if (iterator->fetch(iterator TSRMLS_CC) == 0) { + RETURN_FALSE; + } + } + + item = apc_stack_get(iterator->stack, iterator->stack_idx); + RETURN_ZVAL(item->value, 1, 0); +} +/* }}} */ + +/* {{{ proto string APCIterator::key() */ +PHP_METHOD(apc_iterator, key) { + apc_iterator_item_t *item; + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0 || apc_stack_size(iterator->stack) == 0) { + RETURN_FALSE; + } + + if (apc_stack_size(iterator->stack) == iterator->stack_idx) { + if (iterator->fetch(iterator TSRMLS_CC) == 0) { + RETURN_FALSE; + } + } + + item = apc_stack_get(iterator->stack, iterator->stack_idx); + + if (item->key) { + RETURN_STRINGL(item->key, (item->key_len-1), 1); + } else { + RETURN_LONG(iterator->key_idx); + } +} +/* }}} */ + +/* {{{ proto APCIterator::next() */ +PHP_METHOD(apc_iterator, next) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0 || apc_stack_size(iterator->stack) == 0) { + RETURN_FALSE; + } + + iterator->stack_idx++; + iterator->key_idx++; + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto long APCIterator::getTotalHits() */ +PHP_METHOD(apc_iterator, getTotalHits) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + if (iterator->totals_flag == 0) { + apc_iterator_totals(iterator TSRMLS_CC); + } + + RETURN_LONG(iterator->hits); +} +/* }}} */ + +/* {{{ proto long APCIterator::getTotalSize() */ +PHP_METHOD(apc_iterator, getTotalSize) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + if (iterator->totals_flag == 0) { + apc_iterator_totals(iterator TSRMLS_CC); + } + + RETURN_LONG(iterator->size); +} +/* }}} */ + +/* {{{ proto long APCIterator::getTotalCount() */ +PHP_METHOD(apc_iterator, getTotalCount) { + apc_iterator_t *iterator = (apc_iterator_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (iterator->initialized == 0) { + RETURN_FALSE; + } + + if (iterator->totals_flag == 0) { + apc_iterator_totals(iterator TSRMLS_CC); + } + + RETURN_LONG(iterator->count); +} +/* }}} */ + +/* {{{ arginfo */ +#if (PHP_MAJOR_VERSION >= 6 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3)) +# define PHP_APC_ARGINFO +#else +# define PHP_APC_ARGINFO static +#endif + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_iterator___construct, 0, 0, 1) + ZEND_ARG_INFO(0, cache) + ZEND_ARG_INFO(0, search) + ZEND_ARG_INFO(0, format) + ZEND_ARG_INFO(0, chunk_size) + ZEND_ARG_INFO(0, list) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_iterator_void, 0, 0, 0) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ apc_iterator_functions */ +static zend_function_entry apc_iterator_functions[] = { + PHP_ME(apc_iterator, __construct, arginfo_apc_iterator___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(apc_iterator, rewind, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, current, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, key, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, next, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, valid, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, getTotalHits, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, getTotalSize, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + PHP_ME(apc_iterator, getTotalCount, arginfo_apc_iterator_void, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ apc_iterator_init */ +int apc_iterator_init(int module_number TSRMLS_DC) { + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, APC_ITERATOR_NAME, apc_iterator_functions); + apc_iterator_ce = zend_register_internal_class(&ce TSRMLS_CC); + apc_iterator_ce->create_object = apc_iterator_create; + zend_class_implements(apc_iterator_ce TSRMLS_CC, 1, zend_ce_iterator); + + zend_register_long_constant("APC_LIST_ACTIVE", sizeof("APC_LIST_ACTIVE"), APC_LIST_ACTIVE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_LIST_DELETED", sizeof("APC_LIST_DELETED"), APC_LIST_DELETED, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + + zend_register_long_constant("APC_ITER_TYPE", sizeof("APC_ITER_TYPE"), APC_ITER_TYPE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_KEY", sizeof("APC_ITER_KEY"), APC_ITER_KEY, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_FILENAME", sizeof("APC_ITER_FILENAME"), APC_ITER_FILENAME, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_DEVICE", sizeof("APC_ITER_DEVICE"), APC_ITER_DEVICE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_INODE", sizeof("APC_ITER_INODE"), APC_ITER_INODE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_VALUE", sizeof("APC_ITER_VALUE"), APC_ITER_VALUE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_MD5", sizeof("APC_ITER_MD5"), APC_ITER_MD5, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_NUM_HITS", sizeof("APC_ITER_NUM_HITS"), APC_ITER_NUM_HITS, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_MTIME", sizeof("APC_ITER_MTIME"), APC_ITER_MTIME, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_CTIME", sizeof("APC_ITER_CTIME"), APC_ITER_CTIME, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_DTIME", sizeof("APC_ITER_DTIME"), APC_ITER_DTIME, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_ATIME", sizeof("APC_ITER_ATIME"), APC_ITER_ATIME, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_REFCOUNT", sizeof("APC_ITER_REFCOUNT"), APC_ITER_REFCOUNT, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_MEM_SIZE", sizeof("APC_ITER_MEM_SIZE"), APC_ITER_MEM_SIZE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_TTL", sizeof("APC_ITER_TTL"), APC_ITER_TTL, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_NONE", sizeof("APC_ITER_NONE"), APC_ITER_NONE, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + zend_register_long_constant("APC_ITER_ALL", sizeof("APC_ITER_ALL"), APC_ITER_ALL, CONST_PERSISTENT | CONST_CS, module_number TSRMLS_CC); + + memcpy(&apc_iterator_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + apc_iterator_object_handlers.clone_obj = apc_iterator_clone; + + return SUCCESS; +} +/* }}} */ + + +int apc_iterator_delete(zval *zobj TSRMLS_DC) { + apc_iterator_t *iterator; + zend_class_entry *ce = Z_OBJCE_P(zobj); + apc_iterator_item_t *item; + + if (!ce || !instanceof_function(ce, apc_iterator_ce TSRMLS_CC)) { + apc_error("apc_delete object argument must be instance of APCIterator" TSRMLS_CC); + return 0; + } + iterator = (apc_iterator_t*)zend_object_store_get_object(zobj TSRMLS_CC); + + if (iterator->initialized == 0) { + return 0; + } + + while (iterator->fetch(iterator TSRMLS_CC)) { + while (iterator->stack_idx < apc_stack_size(iterator->stack)) { + item = apc_stack_get(iterator->stack, iterator->stack_idx++); + if (iterator->cache == apc_cache) { + apc_cache_delete(apc_cache, item->filename_key, strlen(item->filename_key) + 1 TSRMLS_CC); + } else { + apc_cache_user_delete(apc_user_cache, item->key, item->key_len TSRMLS_CC); + } + } + } + + return 1; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_iterator.h @@ -0,0 +1,117 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_iterator.h 300979 2010-07-04 10:15:05Z kalle $ */ + +#ifndef APC_ITERATOR_H +#define APC_ITERATOR_H + +#include "apc.h" +#include "apc_stack.h" + +#if HAVE_PCRE || HAVE_BUNDLED_PCRE +/* Deal with problem present until php-5.2.2 where php_pcre.h was not installed correctly */ +# if !HAVE_BUNDLED_PCRE && PHP_MAJOR_VERSION == 5 && (PHP_MINOR_VERSION < 2 || (PHP_MINOR_VERSION == 2 && PHP_RELEASE_VERSION < 2)) +# include "apc_php_pcre.h" +# else +# include "ext/pcre/php_pcre.h" +# endif +# include "ext/standard/php_smart_str.h" +# define ITERATOR_PCRE 1 +#endif + + +#define APC_ITERATOR_NAME "APCIterator" + +#define APC_DEFAULT_CHUNK_SIZE 100 + +#define APC_LIST_ACTIVE 0x1 +#define APC_LIST_DELETED 0x2 + +#define APC_ITER_TYPE (1L << 0) +#define APC_ITER_KEY (1L << 1) +#define APC_ITER_FILENAME (1L << 2) +#define APC_ITER_DEVICE (1L << 3) +#define APC_ITER_INODE (1L << 4) +#define APC_ITER_VALUE (1L << 5) +#define APC_ITER_MD5 (1L << 6) +#define APC_ITER_NUM_HITS (1L << 7) +#define APC_ITER_MTIME (1L << 8) +#define APC_ITER_CTIME (1L << 9) +#define APC_ITER_DTIME (1L << 10) +#define APC_ITER_ATIME (1L << 11) +#define APC_ITER_REFCOUNT (1L << 12) +#define APC_ITER_MEM_SIZE (1L << 13) +#define APC_ITER_TTL (1L << 14) + +#define APC_ITER_NONE (0x00000000L) +#define APC_ITER_ALL (0xffffffffL) + +typedef void* (*apc_iterator_item_cb_t)(slot_t **slot); + + +/* {{{ apc_iterator_t */ +typedef struct _apc_iterator_t { + zend_object obj; /* must always be first */ + short int initialized; /* sanity check in case __construct failed */ + long format; /* format bitmask of the return values ie: key, value, info */ + int (*fetch)(struct _apc_iterator_t *iterator TSRMLS_DC); + /* fetch callback to fetch items from cache slots or lists */ + apc_cache_t *cache; /* cache which we are iterating on */ + long slot_idx; /* index to the slot array or linked list */ + long chunk_size; /* number of entries to pull down per fetch */ + apc_stack_t *stack; /* stack of entries pulled from cache */ + int stack_idx; /* index into the current stack */ +#ifdef ITERATOR_PCRE + pcre *re; /* regex filter on entry identifiers */ +#endif + char *regex; /* original regex expression or NULL */ + int regex_len; /* regex length */ + HashTable *search_hash; /* hash of keys to iterate over */ + long key_idx; /* incrementing index for numerical keys */ + short int totals_flag; /* flag if totals have been calculated */ + long hits; /* hit total */ + size_t size; /* size total */ + long count; /* count total */ +} apc_iterator_t; +/* }}} */ + +/* {{{ apc_iterator_item */ +typedef struct _apc_iterator_item_t { + char *key; /* string key */ + long key_len; /* strlen of key */ + char *filename_key; /* filename key used for deletion */ + zval *value; +} apc_iterator_item_t; +/* }}} */ + + +extern int apc_iterator_init(int module_number TSRMLS_DC); +extern int apc_iterator_delete(zval *zobj TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_lock.h @@ -0,0 +1,101 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_lock.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_LOCK +#define APC_LOCK + +#include "apc.h" +#include "apc_sem.h" +#include "apc_fcntl.h" +#include "apc_pthreadmutex.h" +#include "apc_spin.h" +#ifdef HAVE_CONFIG_H +#include +#endif + +/* {{{ generic locking macros */ +#define CREATE_LOCK(lock) apc_lck_create(NULL, 0, 1, lock) +#define DESTROY_LOCK(lock) apc_lck_destroy(lock) +#define LOCK(lock) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(lock); } +#define RDLOCK(lock) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(lock); } +#define UNLOCK(lock) { apc_lck_unlock(lock); HANDLE_UNBLOCK_INTERRUPTIONS(); } +/* }}} */ + +#if defined(APC_SEM_LOCKS) +#define APC_LOCK_TYPE "IPC Semaphore" +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE 1 +#define apc_lck_t int +#define apc_lck_create(a,b,c,d) d=apc_sem_create((b),(c) TSRMLS_CC) +#define apc_lck_destroy(a) apc_sem_destroy(a) +#define apc_lck_lock(a) apc_sem_lock(a TSRMLS_CC) +#define apc_lck_nb_lock(a) apc_sem_nonblocking_lock(a TSRMLS_CC) +#define apc_lck_rdlock(a) apc_sem_lock(a TSRMLS_CC) +#define apc_lck_unlock(a) apc_sem_unlock(a TSRMLS_CC) +#elif defined(APC_PTHREADMUTEX_LOCKS) +#define APC_LOCK_TYPE "pthread mutex Locks" +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE 1 +#define apc_lck_t pthread_mutex_t +#define apc_lck_create(a,b,c,d) apc_pthreadmutex_create((pthread_mutex_t*)&d TSRMLS_CC) +#define apc_lck_destroy(a) apc_pthreadmutex_destroy(&a) +#define apc_lck_lock(a) apc_pthreadmutex_lock(&a TSRMLS_CC) +#define apc_lck_nb_lock(a) apc_pthreadmutex_nonblocking_lock(&a TSRMLS_CC) +#define apc_lck_rdlock(a) apc_pthreadmutex_lock(&a TSRMLS_CC) +#define apc_lck_unlock(a) apc_pthreadmutex_unlock(&a TSRMLS_CC) +#elif defined(APC_SPIN_LOCKS) +#define APC_LOCK_TYPE "spin Locks" +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE +#define apc_lck_t slock_t +#define apc_lck_create(a,b,c,d) apc_slock_create((slock_t*)&(d)) +#define apc_lck_destroy(a) apc_slock_destroy(&a) +#define apc_lck_lock(a) apc_slock_lock(&a TSRMLS_CC) +#define apc_lck_nb_lock(a) apc_slock_nonblocking_lock(&a) +#define apc_lck_rdlock(a) apc_slock_lock(&a TSRMLS_CC) +#define apc_lck_unlock(a) apc_slock_unlock(&a) +#else +#define APC_LOCK_TYPE "File Locks" +#define RDLOCK_AVAILABLE 1 +#ifdef PHP_WIN32 +#define NONBLOCKING_LOCK_AVAILABLE 0 +#else +#define NONBLOCKING_LOCK_AVAILABLE 1 +#endif +#define apc_lck_t int +#define apc_lck_create(a,b,c,d) d=apc_fcntl_create((a) TSRMLS_CC) +#define apc_lck_destroy(a) apc_fcntl_destroy(a) +#define apc_lck_lock(a) apc_fcntl_lock(a TSRMLS_CC) +#define apc_lck_nb_lock(a) apc_fcntl_nonblocking_lock(a TSRMLS_CC) +#define apc_lck_rdlock(a) apc_fcntl_rdlock(a TSRMLS_CC) +#define apc_lck_unlock(a) apc_fcntl_unlock(a TSRMLS_CC) +#endif + +#endif --- /dev/null +++ b/ext/apc/apc_main.c @@ -0,0 +1,969 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_main.c 304994 2010-10-30 20:33:46Z gopalv $ */ + +#include "apc_php.h" +#include "apc_main.h" +#include "apc.h" +#include "apc_lock.h" +#include "apc_cache.h" +#include "apc_compile.h" +#include "apc_globals.h" +#include "apc_sma.h" +#include "apc_stack.h" +#include "apc_zend.h" +#include "apc_pool.h" +#include "apc_string.h" +#include "SAPI.h" +#include "php_scandir.h" +#include "ext/standard/php_var.h" +#include "ext/standard/md5.h" + +/* {{{ module variables */ + +/* pointer to the original Zend engine compile_file function */ +typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC); +static zend_compile_t *old_compile_file; + +/* }}} */ + +/* {{{ get/set old_compile_file (to interact with other extensions that need the compile hook) */ +static zend_compile_t* set_compile_hook(zend_compile_t *ptr) +{ + zend_compile_t *retval = old_compile_file; + + if (ptr != NULL) old_compile_file = ptr; + return retval; +} +/* }}} */ + +/* {{{ install_function */ +static int install_function(apc_function_t fn, apc_context_t* ctxt, int lazy TSRMLS_DC) +{ + int status; + +#if APC_HAVE_LOOKUP_HOOKS + if(lazy && fn.name[0] != '\0' && strncmp(fn.name, "__autoload", fn.name_len) != 0) { + status = zend_hash_add(APCG(lazy_function_table), + fn.name, + fn.name_len+1, + &fn, + sizeof(apc_function_t), + NULL); +#else + if(0) { +#endif + } else { + zend_function *func = apc_copy_function_for_execution(fn.function, ctxt TSRMLS_CC); + status = zend_hash_add(EG(function_table), + fn.name, + fn.name_len+1, + func, + sizeof(fn.function[0]), + NULL); + efree(func); + } + + if (status == FAILURE) { + /* apc_error("Cannot redeclare %s()" TSRMLS_CC, fn.name); */ + } + + return status; +} +/* }}} */ + +/* {{{ apc_lookup_function_hook */ +int apc_lookup_function_hook(char *name, int len, ulong hash, zend_function **fe) { + apc_function_t *fn; + int status = FAILURE; + apc_context_t ctxt = {0,}; + TSRMLS_FETCH(); + + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_php_malloc, apc_php_free, apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + ctxt.copy = APC_COPY_OUT_OPCODE; + + if(zend_hash_quick_find(APCG(lazy_function_table), name, len, hash, (void**)&fn) == SUCCESS) { + *fe = apc_copy_function_for_execution(fn->function, &ctxt TSRMLS_CC); + status = zend_hash_add(EG(function_table), + fn->name, + fn->name_len+1, + *fe, + sizeof(zend_function), + NULL); + } + + return status; +} +/* }}} */ + +/* {{{ install_class */ +static int install_class(apc_class_t cl, apc_context_t* ctxt, int lazy TSRMLS_DC) +{ + zend_class_entry* class_entry = cl.class_entry; + zend_class_entry* parent = NULL; + int status; + zend_class_entry** allocated_ce = NULL; + + /* Special case for mangled names. Mangled names are unique to a file. + * There is no way two classes with the same mangled name will occur, + * unless a file is included twice. And if in case, a file is included + * twice, all mangled name conflicts can be ignored and the class redeclaration + * error may be deferred till runtime of the corresponding DECLARE_CLASS + * calls. + */ + + if(cl.name_len != 0 && cl.name[0] == '\0') { + if(zend_hash_exists(CG(class_table), cl.name, cl.name_len+1)) { + return SUCCESS; + } + } + + if(lazy && cl.name_len != 0 && cl.name[0] != '\0') { + status = zend_hash_add(APCG(lazy_class_table), + cl.name, + cl.name_len+1, + &cl, + sizeof(apc_class_t), + NULL); + if(status == FAILURE) { + zend_error(E_ERROR, "Cannot redeclare class %s", cl.name); + } + return status; + } + + /* + * XXX: We need to free this somewhere... + */ + allocated_ce = apc_php_malloc(sizeof(zend_class_entry*) TSRMLS_CC); + + if(!allocated_ce) { + return FAILURE; + } + + *allocated_ce = + class_entry = + apc_copy_class_entry_for_execution(cl.class_entry, ctxt TSRMLS_CC); + + + /* restore parent class pointer for compile-time inheritance */ + if (cl.parent_name != NULL) { + zend_class_entry** parent_ptr = NULL; + /* + * __autoload brings in the old issues with mixed inheritance. + * When a statically inherited class triggers autoload, it runs + * afoul of a potential require_once "parent.php" in the previous + * line, which when executed provides the parent class, but right + * now goes and hits __autoload which could fail. + * + * missing parent == re-compile. + * + * whether __autoload is enabled or not, because __autoload errors + * cause php to die. + * + * Aside: Do NOT pass *strlen(cl.parent_name)+1* because + * zend_lookup_class_ex does it internally anyway! + */ + status = zend_lookup_class_ex(cl.parent_name, + strlen(cl.parent_name), +#ifdef ZEND_ENGINE_2_4 + NULL, +#endif + 0, + &parent_ptr TSRMLS_CC); + if (status == FAILURE) { + if(APCG(report_autofilter)) { + apc_warning("Dynamic inheritance detected for class %s" TSRMLS_CC, cl.name); + } + class_entry->parent = NULL; + return status; + } + else { + parent = *parent_ptr; + class_entry->parent = parent; + zend_do_inheritance(class_entry, parent TSRMLS_CC); + } + + + } + + status = zend_hash_add(EG(class_table), + cl.name, + cl.name_len+1, + allocated_ce, + sizeof(zend_class_entry*), + NULL); + + if (status == FAILURE) { + apc_error("Cannot redeclare class %s" TSRMLS_CC, cl.name); + } + return status; +} +/* }}} */ + +/* {{{ apc_lookup_class_hook */ +int apc_lookup_class_hook(char *name, int len, ulong hash, zend_class_entry ***ce) { + + apc_class_t *cl; + apc_context_t ctxt = {0,}; + TSRMLS_FETCH(); + + if(zend_is_compiling(TSRMLS_C)) { return FAILURE; } + + if(zend_hash_quick_find(APCG(lazy_class_table), name, len, hash, (void**)&cl) == FAILURE) { + return FAILURE; + } + + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_php_malloc, apc_php_free, apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + ctxt.copy = APC_COPY_OUT_OPCODE; + + if(install_class(*cl, &ctxt, 0 TSRMLS_CC) == FAILURE) { + apc_warning("apc_lookup_class_hook: could not install %s" TSRMLS_CC, name); + return FAILURE; + } + + if(zend_hash_quick_find(EG(class_table), name, len, hash, (void**)ce) == FAILURE) { + apc_warning("apc_lookup_class_hook: known error trying to fetch class %s" TSRMLS_CC, name); + return FAILURE; + } + + return SUCCESS; + +} +/* }}} */ + +/* {{{ uninstall_class */ +static int uninstall_class(apc_class_t cl TSRMLS_DC) +{ + int status; + + status = zend_hash_del(EG(class_table), + cl.name, + cl.name_len+1); + if (status == FAILURE) { + apc_error("Cannot delete class %s" TSRMLS_CC, cl.name); + } + return status; +} +/* }}} */ + +/* {{{ copy_function_name (taken from zend_builtin_functions.c to ensure future compatibility with APC) */ +static int copy_function_name(apc_function_t *pf TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + zval *internal_ar = va_arg(args, zval *), + *user_ar = va_arg(args, zval *); + zend_function *func = pf->function; + + if (hash_key->nKeyLength == 0 || hash_key->arKey[0] == 0) { + return 0; + } + + if (func->type == ZEND_INTERNAL_FUNCTION) { + add_next_index_stringl(internal_ar, hash_key->arKey, hash_key->nKeyLength-1, 1); + } else if (func->type == ZEND_USER_FUNCTION) { + add_next_index_stringl(user_ar, hash_key->arKey, hash_key->nKeyLength-1, 1); + } + + return 0; +} + +/* {{{ copy_class_or_interface_name (taken from zend_builtin_functions.c to ensure future compatibility with APC) */ +static int copy_class_or_interface_name(apc_class_t *cl TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + zval *array = va_arg(args, zval *); + zend_uint mask = va_arg(args, zend_uint); + zend_uint comply = va_arg(args, zend_uint); + zend_uint comply_mask = (comply)? mask:0; + zend_class_entry *ce = cl->class_entry; + + if ((hash_key->nKeyLength==0 || hash_key->arKey[0]!=0) + && (comply_mask == (ce->ce_flags & mask))) { + add_next_index_stringl(array, ce->name, ce->name_length, 1); + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +/* }}} */ + +/* {{{ apc_defined_function_hook */ +int apc_defined_function_hook(zval *internal, zval *user) { + TSRMLS_FETCH(); + zend_hash_apply_with_arguments(APCG(lazy_function_table) +#ifdef ZEND_ENGINE_2_3 + TSRMLS_CC +#endif + ,(apply_func_args_t) copy_function_name, 2, internal, user); + return 1; +} +/* }}} */ + +/* {{{ apc_declared_class_hook */ +int apc_declared_class_hook(zval *classes, zend_uint mask, zend_uint comply) { + TSRMLS_FETCH(); + zend_hash_apply_with_arguments(APCG(lazy_class_table) +#ifdef ZEND_ENGINE_2_3 + TSRMLS_CC +#endif + , (apply_func_args_t) copy_class_or_interface_name, 3, classes, mask, comply); + return 1; +} +/* }}} */ + +/* {{{ cached_compile */ +static zend_op_array* cached_compile(zend_file_handle* h, + int type, + apc_context_t* ctxt TSRMLS_DC) +{ + apc_cache_entry_t* cache_entry; + int i, ii; + + cache_entry = (apc_cache_entry_t*) apc_stack_top(APCG(cache_stack)); + assert(cache_entry != NULL); + + if (cache_entry->data.file.classes) { + int lazy_classes = APCG(lazy_classes); + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { + if(install_class(cache_entry->data.file.classes[i], ctxt, lazy_classes TSRMLS_CC) == FAILURE) { + goto default_compile; + } + } + } + + if (cache_entry->data.file.functions) { + int lazy_functions = APCG(lazy_functions); + for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) { + install_function(cache_entry->data.file.functions[i], ctxt, lazy_functions TSRMLS_CC); + } + } + + apc_do_halt_compiler_register(cache_entry->data.file.filename, cache_entry->data.file.halt_offset TSRMLS_CC); + + + return apc_copy_op_array_for_execution(NULL, cache_entry->data.file.op_array, ctxt TSRMLS_CC); + +default_compile: + + if(cache_entry->data.file.classes) { + for(ii = 0; ii < i ; ii++) { + uninstall_class(cache_entry->data.file.classes[ii] TSRMLS_CC); + } + } + + apc_stack_pop(APCG(cache_stack)); /* pop out cache_entry */ + + apc_cache_release(apc_cache, cache_entry TSRMLS_CC); + + /* cannot free up cache data yet, it maybe in use */ + + return NULL; +} +/* }}} */ + +/* {{{ apc_compile_cache_entry */ +zend_bool apc_compile_cache_entry(apc_cache_key_t key, zend_file_handle* h, int type, time_t t, zend_op_array** op_array, apc_cache_entry_t** cache_entry TSRMLS_DC) { + int num_functions, num_classes; + apc_function_t* alloc_functions; + zend_op_array* alloc_op_array; + apc_class_t* alloc_classes; + char *path; + apc_context_t ctxt; + + /* remember how many functions and classes existed before compilation */ + num_functions = zend_hash_num_elements(CG(function_table)); + num_classes = zend_hash_num_elements(CG(class_table)); + + /* compile the file using the default compile function, * + * we set *op_array here so we return opcodes during * + * a failure. We should not return prior to this line. */ + *op_array = old_compile_file(h, type TSRMLS_CC); + if (*op_array == NULL) { + return FAILURE; + } + + ctxt.pool = apc_pool_create(APC_MEDIUM_POOL, apc_sma_malloc, apc_sma_free, + apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + if (!ctxt.pool) { + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + return FAILURE; + } + ctxt.copy = APC_COPY_IN_OPCODE; + + if(APCG(file_md5)) { + int n; + unsigned char buf[1024]; + PHP_MD5_CTX context; + php_stream *stream; + char *filename; + + if(h->opened_path) { + filename = h->opened_path; + } else { + filename = h->filename; + } + stream = php_stream_open_wrapper(filename, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL); + if(stream) { + PHP_MD5Init(&context); + while((n = php_stream_read(stream, (char*)buf, sizeof(buf))) > 0) { + PHP_MD5Update(&context, buf, n); + } + PHP_MD5Final(key.md5, &context); + php_stream_close(stream); + if(n<0) { + apc_warning("Error while reading '%s' for md5 generation." TSRMLS_CC, filename); + } + } else { + apc_warning("Unable to open '%s' for md5 generation." TSRMLS_CC, filename); + } + } + + if(!(alloc_op_array = apc_copy_op_array(NULL, *op_array, &ctxt TSRMLS_CC))) { + goto freepool; + } + + if(!(alloc_functions = apc_copy_new_functions(num_functions, &ctxt TSRMLS_CC))) { + goto freepool; + } + if(!(alloc_classes = apc_copy_new_classes(*op_array, num_classes, &ctxt TSRMLS_CC))) { + goto freepool; + } + + path = h->opened_path; + if(!path) path=h->filename; + + apc_debug("2. h->opened_path=[%s] h->filename=[%s]\n" TSRMLS_CC, h->opened_path?h->opened_path:"null",h->filename); + + if(!(*cache_entry = apc_cache_make_file_entry(path, alloc_op_array, alloc_functions, alloc_classes, &ctxt TSRMLS_CC))) { + goto freepool; + } + + return SUCCESS; + +freepool: + apc_pool_destroy(ctxt.pool TSRMLS_CC); + ctxt.pool = NULL; + + return FAILURE; + +} +/* }}} */ + +/* {{{ my_compile_file + Overrides zend_compile_file */ +static zend_op_array* my_compile_file(zend_file_handle* h, + int type TSRMLS_DC) +{ + apc_cache_key_t key; + apc_cache_entry_t* cache_entry; + zend_op_array* op_array = NULL; + time_t t; + apc_context_t ctxt = {0,}; + int bailout=0; + const char* filename = NULL; + + if (!APCG(enabled) || apc_cache_busy(apc_cache)) { + return old_compile_file(h, type TSRMLS_CC); + } + + if(h->opened_path) { + filename = h->opened_path; + } else { + filename = h->filename; + } + + /* check our regular expression filters */ + if (APCG(filters) && APCG(compiled_filters) && filename) { + int ret = apc_regex_match_array(APCG(compiled_filters), filename); + + if(ret == APC_NEGATIVE_MATCH || (ret != APC_POSITIVE_MATCH && !APCG(cache_by_default))) { + return old_compile_file(h, type TSRMLS_CC); + } + } else if(!APCG(cache_by_default)) { + return old_compile_file(h, type TSRMLS_CC); + } + APCG(current_cache) = apc_cache; + + + t = apc_time(); + + apc_debug("1. h->opened_path=[%s] h->filename=[%s]\n" TSRMLS_CC, h->opened_path?h->opened_path:"null",h->filename); + + /* try to create a cache key; if we fail, give up on caching */ + if (!apc_cache_make_file_key(&key, h->filename, PG(include_path), t TSRMLS_CC)) { + return old_compile_file(h, type TSRMLS_CC); + } + + if(!APCG(force_file_update)) { + /* search for the file in the cache */ + cache_entry = apc_cache_find(apc_cache, key, t TSRMLS_CC); + ctxt.force_update = 0; + } else { + cache_entry = NULL; + ctxt.force_update = 1; + } + + if (cache_entry != NULL) { + int dummy = 1; + + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_php_malloc, apc_php_free, + apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + if (!ctxt.pool) { + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + return old_compile_file(h, type TSRMLS_CC); + } + ctxt.copy = APC_COPY_OUT_OPCODE; + + zend_hash_add(&EG(included_files), cache_entry->data.file.filename, + strlen(cache_entry->data.file.filename)+1, + (void *)&dummy, sizeof(int), NULL); + + apc_stack_push(APCG(cache_stack), cache_entry TSRMLS_CC); + op_array = cached_compile(h, type, &ctxt TSRMLS_CC); + + if(op_array) { +#ifdef APC_FILEHITS + /* If the file comes from the cache, add it to the global request file list */ + add_next_index_string(APCG(filehits), h->filename, 1); +#endif + /* this is an unpool, which has no cleanup - this only free's the pool header */ + apc_pool_destroy(ctxt.pool TSRMLS_CC); + + /* We might leak fds without this hack */ + if (h->type != ZEND_HANDLE_FILENAME) { + zend_llist_add_element(&CG(open_files), h); + } + return op_array; + } + if(APCG(report_autofilter)) { + apc_warning("Autofiltering %s" TSRMLS_CC, + (h->opened_path ? h->opened_path : h->filename)); + apc_warning("Recompiling %s" TSRMLS_CC, cache_entry->data.file.filename); + } + /* TODO: check what happens with EG(included_files) */ + } + + /* Make sure the mtime reflects the files last known mtime, and we respect max_file_size in the case of fpstat==0 */ + if(key.type == APC_CACHE_KEY_FPFILE) { + apc_fileinfo_t fileinfo; + struct stat *tmp_buf = NULL; + if(!strcmp(SG(request_info).path_translated, h->filename)) { + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ + } + if(tmp_buf) { + fileinfo.st_buf.sb = *tmp_buf; + } else { + if (apc_search_paths(h->filename, PG(include_path), &fileinfo TSRMLS_CC) != 0) { + apc_debug("Stat failed %s - bailing (%s) (%d)\n" TSRMLS_CC,h->filename,SG(request_info).path_translated); + return old_compile_file(h, type TSRMLS_CC); + } + } + if (APCG(max_file_size) < fileinfo.st_buf.sb.st_size) { + apc_debug("File is too big %s (%ld) - bailing\n" TSRMLS_CC, h->filename, fileinfo.st_buf.sb.st_size); + return old_compile_file(h, type TSRMLS_CC); + } + key.mtime = fileinfo.st_buf.sb.st_mtime; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + if(!apc_cache_write_lock(apc_cache TSRMLS_CC)) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + return old_compile_file(h, type TSRMLS_CC); + } + } +#endif + + zend_try { + if (apc_compile_cache_entry(key, h, type, t, &op_array, &cache_entry TSRMLS_CC) == SUCCESS) { + ctxt.pool = cache_entry->pool; + ctxt.copy = APC_COPY_IN_OPCODE; + if (apc_cache_insert(apc_cache, key, cache_entry, &ctxt, t TSRMLS_CC) != 1) { + apc_pool_destroy(ctxt.pool TSRMLS_CC); + ctxt.pool = NULL; + } + } + } zend_catch { + bailout=1; /* in the event of a bailout, ensure we don't create a dead-lock */ + } zend_end_try(); + + APCG(current_cache) = NULL; + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache TSRMLS_CC); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + + if (bailout) zend_bailout(); + + return op_array; +} +/* }}} */ + +/* {{{ data preload */ + +extern int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int ttl, const int exclusive TSRMLS_DC); + +static zval* data_unserialize(const char *filename TSRMLS_DC) +{ + zval* retval; + long len = 0; + struct stat sb; + char *contents, *tmp; + FILE *fp; + php_unserialize_data_t var_hash; + + if(VCWD_STAT(filename, &sb) == -1) { + return NULL; + } + + fp = fopen(filename, "rb"); + + len = sizeof(char)*sb.st_size; + + tmp = contents = malloc(len); + + if(!contents) { + return NULL; + } + + if(fread(contents, 1, len, fp) < 1) { + free(contents); + return NULL; + } + + MAKE_STD_ZVAL(retval); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + + /* I wish I could use json */ + if(!php_var_unserialize(&retval, (const unsigned char**)&tmp, (const unsigned char*)(contents+len), &var_hash TSRMLS_CC)) { + zval_ptr_dtor(&retval); + return NULL; + } + + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + + free(contents); + fclose(fp); + + return retval; +} + +static int apc_load_data(const char *data_file TSRMLS_DC) +{ + char *p; + char key[MAXPATHLEN] = {0,}; + unsigned int key_len; + zval *data; + + p = strrchr(data_file, DEFAULT_SLASH); + + if(p && p[1]) { + strlcpy(key, p+1, sizeof(key)); + p = strrchr(key, '.'); + + if(p) { + p[0] = '\0'; + key_len = strlen(key); + + data = data_unserialize(data_file TSRMLS_CC); + if(data) { + _apc_store(key, key_len, data, 0, 1 TSRMLS_CC); + } + return 1; + } + } + + return 0; +} + +static int apc_walk_dir(const char *path TSRMLS_DC) +{ + char file[MAXPATHLEN]={0,}; + int ndir, i; + char *p = NULL; + struct dirent **namelist = NULL; + + if ((ndir = php_scandir(path, &namelist, 0, php_alphasort)) > 0) + { + for (i = 0; i < ndir; i++) + { + /* check for extension */ + if (!(p = strrchr(namelist[i]->d_name, '.')) + || (p && strcmp(p, ".data"))) + { + free(namelist[i]); + continue; + } + snprintf(file, MAXPATHLEN, "%s%c%s", + path, DEFAULT_SLASH, namelist[i]->d_name); + if(!apc_load_data(file TSRMLS_CC)) + { + /* print error */ + } + free(namelist[i]); + } + free(namelist); + } + + return 1; +} + +void apc_data_preload(TSRMLS_D) +{ + if(!APCG(preload_path)) return; + + apc_walk_dir(APCG(preload_path) TSRMLS_CC); +} +/* }}} */ + +/* {{{ module init and shutdown */ + +int apc_module_init(int module_number TSRMLS_DC) +{ + /* apc initialization */ +#if APC_MMAP + apc_sma_init(APCG(shm_segments), APCG(shm_size), APCG(mmap_file_mask) TSRMLS_CC); +#else + apc_sma_init(APCG(shm_segments), APCG(shm_size), NULL TSRMLS_CC); +#endif + apc_cache = apc_cache_create(APCG(num_files_hint), APCG(gc_ttl), APCG(ttl) TSRMLS_CC); + apc_user_cache = apc_cache_create(APCG(user_entries_hint), APCG(gc_ttl), APCG(user_ttl) TSRMLS_CC); + + /* override compilation */ + old_compile_file = zend_compile_file; + zend_compile_file = my_compile_file; + REGISTER_LONG_CONSTANT("\000apc_magic", (long)&set_compile_hook, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("\000apc_compile_file", (long)&my_compile_file, CONST_PERSISTENT | CONST_CS); + + apc_pool_init(); + + apc_data_preload(TSRMLS_C); + +#if APC_HAVE_LOOKUP_HOOKS + if(APCG(lazy_functions)) { + zend_set_lookup_function_hook(apc_lookup_function_hook TSRMLS_CC); + zend_set_defined_function_hook(apc_defined_function_hook TSRMLS_CC); + } + if(APCG(lazy_classes)) { + zend_set_lookup_class_hook(apc_lookup_class_hook TSRMLS_CC); + zend_set_declared_class_hook(apc_declared_class_hook TSRMLS_CC); + } +#else + if(APCG(lazy_functions) || APCG(lazy_classes)) { + apc_warning("Lazy function/class loading not available with this version of PHP, please disable APC lazy loading." TSRMLS_CC); + APCG(lazy_functions) = APCG(lazy_classes) = 0; + } +#endif + +#ifdef ZEND_ENGINE_2_4 + apc_interned_strings_init(TSRMLS_C); +#endif + + APCG(initialized) = 1; + return 0; +} + +int apc_module_shutdown(TSRMLS_D) +{ + if (!APCG(initialized)) + return 0; + + /* restore compilation */ + zend_compile_file = old_compile_file; + + /* + * In case we got interrupted by a SIGTERM or something else during execution + * we may have cache entries left on the stack that we need to check to make + * sure that any functions or classes these may have added to the global function + * and class tables are removed before we blow away the memory that hold them. + * + * This is merely to remove memory leak warnings - as the process is terminated + * immediately after shutdown. The following while loop can be removed without + * affecting anything else. + */ + while (apc_stack_size(APCG(cache_stack)) > 0) { + int i; + apc_cache_entry_t* cache_entry = (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack)); + if (cache_entry->data.file.functions) { + for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) { + zend_hash_del(EG(function_table), + cache_entry->data.file.functions[i].name, + cache_entry->data.file.functions[i].name_len+1); + } + } + if (cache_entry->data.file.classes) { + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { + zend_hash_del(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1); + } + } + apc_cache_release(apc_cache, cache_entry TSRMLS_CC); + } + + apc_cache_destroy(apc_cache TSRMLS_CC); + apc_cache_destroy(apc_user_cache TSRMLS_CC); + apc_sma_cleanup(TSRMLS_C); + +#ifdef ZEND_ENGINE_2_4 + apc_interned_strings_shutdown(TSRMLS_C); +#endif + + APCG(initialized) = 0; + return 0; +} + +/* }}} */ + +/* {{{ process init and shutdown */ +int apc_process_init(int module_number TSRMLS_DC) +{ + return 0; +} + +int apc_process_shutdown(TSRMLS_D) +{ + return 0; +} +/* }}} */ + + +/* {{{ apc_deactivate */ +static void apc_deactivate(TSRMLS_D) +{ + /* The execution stack was unwound, which prevented us from decrementing + * the reference counts on active cache entries in `my_execute`. + */ + while (apc_stack_size(APCG(cache_stack)) > 0) { + int i; + zend_class_entry* zce = NULL; + void ** centry = (void*)(&zce); + zend_class_entry** pzce = NULL; + + apc_cache_entry_t* cache_entry = + (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack)); + + if (cache_entry->data.file.classes) { + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { + centry = (void**)&pzce; /* a triple indirection to get zend_class_entry*** */ + if(zend_hash_find(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1, + (void**)centry) == FAILURE) + { + /* double inclusion of conditional classes ends up failing + * this lookup the second time around. + */ + continue; + } + + zce = *pzce; + + zend_hash_del(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1); + + apc_free_class_entry_after_execution(zce TSRMLS_CC); + } + } + apc_cache_release(apc_cache, cache_entry TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ request init and shutdown */ + +int apc_request_init(TSRMLS_D) +{ + apc_stack_clear(APCG(cache_stack)); + if (!APCG(compiled_filters) && APCG(filters)) { + /* compile regex filters here to avoid race condition between MINIT of PCRE and APC. + * This should be moved to apc_cache_create() if this race condition between modules is resolved */ + APCG(compiled_filters) = apc_regex_compile_array(APCG(filters) TSRMLS_CC); + } + +#if APC_HAVE_LOOKUP_HOOKS + if(APCG(lazy_functions)) { + APCG(lazy_function_table) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(lazy_function_table), 0, NULL, NULL, 0); + } + if(APCG(lazy_classes)) { + APCG(lazy_class_table) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(lazy_class_table), 0, NULL, NULL, 0); + } +#endif + +#ifdef APC_FILEHITS + ALLOC_INIT_ZVAL(APCG(filehits)); + array_init(APCG(filehits)); +#endif + + return 0; +} + +int apc_request_shutdown(TSRMLS_D) +{ + +#if APC_HAVE_LOOKUP_HOOKS + if(APCG(lazy_class_table)) { + zend_hash_destroy(APCG(lazy_class_table)); + efree(APCG(lazy_class_table)); + } + if(APCG(lazy_function_table)) { + zend_hash_destroy(APCG(lazy_function_table)); + efree(APCG(lazy_function_table)); + } +#endif + + apc_deactivate(TSRMLS_C); + +#ifdef APC_FILEHITS + zval_ptr_dtor(&APCG(filehits)); +#endif + + return 0; +} + +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_main.h @@ -0,0 +1,74 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_main.h 300979 2010-07-04 10:15:05Z kalle $ */ + +#ifndef APC_MAIN_H +#define APC_MAIN_H + +#include "apc_pool.h" + +/* + * This module provides the primary interface between PHP and APC. + */ + +extern int apc_module_init(int module_number TSRMLS_DC); +extern int apc_module_shutdown(TSRMLS_D); +extern int apc_process_init(int module_number TSRMLS_DC); +extern int apc_process_shutdown(TSRMLS_D); +extern int apc_request_init(TSRMLS_D); +extern int apc_request_shutdown(TSRMLS_D); + +typedef enum _apc_copy_type { + APC_NO_COPY = 0, + APC_COPY_IN_OPCODE, + APC_COPY_OUT_OPCODE, + APC_COPY_IN_USER, + APC_COPY_OUT_USER +} apc_copy_type; + +typedef struct _apc_context_t +{ + apc_pool *pool; + apc_copy_type copy; + unsigned int force_update:1; +} apc_context_t; + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_mmap.c @@ -0,0 +1,175 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_mmap.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc.h" +#include "apc_mmap.h" +#include "apc_lock.h" + +#if APC_MMAP + +#include +#include +#include + +/* + * Some operating systems (like FreeBSD) have a MAP_NOSYNC flag that + * tells whatever update daemons might be running to not flush dirty + * vm pages to disk unless absolutely necessary. My guess is that + * most systems that don't have this probably default to only synching + * to disk when absolutely necessary. + */ +#ifndef MAP_NOSYNC +#define MAP_NOSYNC 0 +#endif + +/* support for systems where MAP_ANONYMOUS is defined but not MAP_ANON, ie: HP-UX bug #14615 */ +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +#endif + +apc_segment_t apc_mmap(char *file_mask, size_t size TSRMLS_DC) +{ + apc_segment_t segment; + + int fd = -1; + int flags = MAP_SHARED | MAP_NOSYNC; + int remap = 1; + + /* If no filename was provided, do an anonymous mmap */ + if(!file_mask || (file_mask && !strlen(file_mask))) { +#if !defined(MAP_ANON) + apc_error("Anonymous mmap does not apear to be available on this system (MAP_ANON/MAP_ANONYMOUS). Please see the apc.mmap_file_mask INI option." TSRMLS_CC); +#else + fd = -1; + flags = MAP_SHARED | MAP_ANON; + remap = 0; +#endif + } else if(!strcmp(file_mask,"/dev/zero")) { + fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); + if(fd == -1) { + apc_error("apc_mmap: open on /dev/zero failed:" TSRMLS_CC); + goto error; + } + remap = 0; /* cannot remap */ + } else if(strstr(file_mask,".shm")) { + /* + * If the filemask contains .shm we try to do a POSIX-compliant shared memory + * backed mmap which should avoid synchs on some platforms. At least on + * FreeBSD this implies MAP_NOSYNC and on Linux it is equivalent of mmap'ing + * a file in a mounted shmfs. For this to work on Linux you need to make sure + * you actually have shmfs mounted. Also on Linux, make sure the file_mask you + * pass in has a leading / and no other /'s. eg. /apc.shm.XXXXXX + * On FreeBSD these are mapped onto the regular filesystem so you can put whatever + * path you want here. + */ + if(!mktemp(file_mask)) { + apc_error("apc_mmap: mktemp on %s failed:" TSRMLS_CC, file_mask); + goto error; + } + fd = shm_open(file_mask, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + if(fd == -1) { + apc_error("apc_mmap: shm_open on %s failed:" TSRMLS_CC, file_mask); + goto error; + } + if (ftruncate(fd, size) < 0) { + close(fd); + shm_unlink(file_mask); + apc_error("apc_mmap: ftruncate failed:" TSRMLS_CC); + goto error; + } + shm_unlink(file_mask); + } else { + /* + * Otherwise we do a normal filesystem mmap + */ + fd = mkstemp(file_mask); + if(fd == -1) { + apc_error("apc_mmap: mkstemp on %s failed:" TSRMLS_CC, file_mask); + goto error; + } + if (ftruncate(fd, size) < 0) { + close(fd); + unlink(file_mask); + apc_error("apc_mmap: ftruncate failed:" TSRMLS_CC); + goto error; + } + unlink(file_mask); + } + + segment.shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); + +#ifdef APC_MEMPROTECT + if(remap) { + segment.roaddr = (void *)mmap(NULL, size, PROT_READ, flags, fd, 0); + } else { + segment.roaddr = NULL; + } +#endif + + if((long)segment.shmaddr == -1) { + apc_error("apc_mmap: mmap failed:" TSRMLS_CC); + } + + if(fd != -1) close(fd); + + return segment; + +error: + + segment.shmaddr = (void*)-1; +#ifdef APC_MEMPROTECT + segment.roaddr = NULL; +#endif + return segment; +} + +void apc_unmap(apc_segment_t *segment TSRMLS_DC) +{ + if (munmap(segment->shmaddr, segment->size) < 0) { + apc_warning("apc_unmap: munmap failed:" TSRMLS_CC); + } + +#ifdef APC_MEMPROTECT + if (segment->roaddr && munmap(segment->roaddr, segment->size) < 0) { + apc_warning("apc_unmap: munmap failed:" TSRMLS_CC); + } +#endif + +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_mmap.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Gopal V | + +----------------------------------------------------------------------+ + + 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: apc_mmap.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_MMAP_H +#define APC_MMAP_H + +#include + +#include "apc.h" +#include "apc_sma.h" + +/* Wrapper functions for shared memory mapped files */ + +#if APC_MMAP +apc_segment_t apc_mmap(char *file_mask, size_t size TSRMLS_DC); +void apc_unmap(apc_segment_t* segment TSRMLS_DC); +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc.php @@ -0,0 +1,1362 @@ + | + | Rasmus Lerdorf | + | Ilia Alshanetsky | + +----------------------------------------------------------------------+ + + All other licensing and usage conditions are those of the PHP Group. + + */ + +$VERSION='$Id: apc.php 304410 2010-10-15 11:21:07Z gopalv $'; + +////////// READ OPTIONAL CONFIGURATION FILE //////////// +if (file_exists("apc.conf.php")) include("apc.conf.php"); +//////////////////////////////////////////////////////// + +////////// BEGIN OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////// + +defaults('USE_AUTHENTICATION',1); // Use (internal) authentication - best choice if + // no other authentication is available + // If set to 0: + // There will be no further authentication. You + // will have to handle this by yourself! + // If set to 1: + // You need to change ADMIN_PASSWORD to make + // this work! +defaults('ADMIN_USERNAME','apc'); // Admin Username +defaults('ADMIN_PASSWORD','password'); // Admin Password - CHANGE THIS TO ENABLE!!! + +// (beckerr) I'm using a clear text password here, because I've no good idea how to let +// users generate a md5 or crypt password in a easy way to fill it in above + +//defaults('DATE_FORMAT', "d.m.Y H:i:s"); // German +defaults('DATE_FORMAT', 'Y/m/d H:i:s'); // US + +defaults('GRAPH_SIZE',200); // Image size + +//defaults('PROXY', 'tcp://127.0.0.1:8080'); + +////////// END OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////// + + +// "define if not defined" +function defaults($d,$v) { + if (!defined($d)) define($d,$v); // or just @define(...) +} + +// rewrite $PHP_SELF to block XSS attacks +// +$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],''), ENT_QUOTES, 'UTF-8') : ''; +$time = time(); +$host = php_uname('n'); +if($host) { $host = '('.$host.')'; } +if (isset($_SERVER['SERVER_ADDR'])) { + $host .= ' ('.$_SERVER['SERVER_ADDR'].')'; +} + +// operation constants +define('OB_HOST_STATS',1); +define('OB_SYS_CACHE',2); +define('OB_USER_CACHE',3); +define('OB_SYS_CACHE_DIR',4); +define('OB_VERSION_CHECK',9); + +// check validity of input variables +$vardom=array( + 'OB' => '/^\d+$/', // operational mode switch + 'CC' => '/^[01]$/', // clear cache requested + 'DU' => '/^.*$/', // Delete User Key + 'SH' => '/^[a-z0-9]+$/', // shared object description + + 'IMG' => '/^[123]$/', // image to generate + 'LO' => '/^1$/', // login requested + + 'COUNT' => '/^\d+$/', // number of line displayed in list + 'SCOPE' => '/^[AD]$/', // list view scope + 'SORT1' => '/^[AHSMCDTZ]$/', // first sort key + 'SORT2' => '/^[DA]$/', // second sort key + 'AGGR' => '/^\d+$/', // aggregation by dir level + 'SEARCH' => '~^[a-zA-Z0-1/_.-]*$~' // aggregation by dir level +); + +// default cache mode +$cache_mode='opcode'; + +// cache scope +$scope_list=array( + 'A' => 'cache_list', + 'D' => 'deleted_list' +); + +// handle POST and GET requests +if (empty($_REQUEST)) { + if (!empty($_GET) && !empty($_POST)) { + $_REQUEST = array_merge($_GET, $_POST); + } else if (!empty($_GET)) { + $_REQUEST = $_GET; + } else if (!empty($_POST)) { + $_REQUEST = $_POST; + } else { + $_REQUEST = array(); + } +} + +// check parameter syntax +foreach($vardom as $var => $dom) { + if (!isset($_REQUEST[$var])) { + $MYREQUEST[$var]=NULL; + } else if (!is_array($_REQUEST[$var]) && preg_match($dom.'D',$_REQUEST[$var])) { + $MYREQUEST[$var]=$_REQUEST[$var]; + } else { + $MYREQUEST[$var]=$_REQUEST[$var]=NULL; + } +} + +// check parameter sematics +if (empty($MYREQUEST['SCOPE'])) $MYREQUEST['SCOPE']="A"; +if (empty($MYREQUEST['SORT1'])) $MYREQUEST['SORT1']="H"; +if (empty($MYREQUEST['SORT2'])) $MYREQUEST['SORT2']="D"; +if (empty($MYREQUEST['OB'])) $MYREQUEST['OB']=OB_HOST_STATS; +if (!isset($MYREQUEST['COUNT'])) $MYREQUEST['COUNT']=20; +if (!isset($scope_list[$MYREQUEST['SCOPE']])) $MYREQUEST['SCOPE']='A'; + +$MY_SELF= + "$PHP_SELF". + "?SCOPE=".$MYREQUEST['SCOPE']. + "&SORT1=".$MYREQUEST['SORT1']. + "&SORT2=".$MYREQUEST['SORT2']. + "&COUNT=".$MYREQUEST['COUNT']; +$MY_SELF_WO_SORT= + "$PHP_SELF". + "?SCOPE=".$MYREQUEST['SCOPE']. + "&COUNT=".$MYREQUEST['COUNT']; + +// authentication needed? +// +if (!USE_AUTHENTICATION) { + $AUTHENTICATED=1; +} else { + $AUTHENTICATED=0; + if (ADMIN_PASSWORD!='password' && ($MYREQUEST['LO'] == 1 || isset($_SERVER['PHP_AUTH_USER']))) { + + if (!isset($_SERVER['PHP_AUTH_USER']) || + !isset($_SERVER['PHP_AUTH_PW']) || + $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME || + $_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) { + Header("WWW-Authenticate: Basic realm=\"APC Login\""); + Header("HTTP/1.0 401 Unauthorized"); + + echo << +

Rejected!

+ Wrong Username or Password!
 
  + Continue... + +EOB; + exit; + + } else { + $AUTHENTICATED=1; + } + } +} + +// select cache mode +if ($AUTHENTICATED && $MYREQUEST['OB'] == OB_USER_CACHE) { + $cache_mode='user'; +} +// clear cache +if ($AUTHENTICATED && isset($MYREQUEST['CC']) && $MYREQUEST['CC']) { + apc_clear_cache($cache_mode); +} + +if ($AUTHENTICATED && !empty($MYREQUEST['DU'])) { + apc_delete($MYREQUEST['DU']); +} + +if(!function_exists('apc_cache_info') || !($cache=@apc_cache_info($cache_mode))) { + echo "No cache info available. APC does not appear to be running."; + exit; +} + +$cache_user = apc_cache_info('user', 1); +$mem=apc_sma_info(); +if(!$cache['num_hits']) { $cache['num_hits']=1; $time++; } // Avoid division by 0 errors on a cache clear + +// don't cache this page +// +header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); // HTTP/1.0 + +function duration($ts) { + global $time; + $years = (int)((($time - $ts)/(7*86400))/52.177457); + $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400)); + $weeks = (int)(($rem)/(7*86400)); + $days = (int)(($rem)/86400) - $weeks*7; + $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24; + $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60; + $str = ''; + if($years==1) $str .= "$years year, "; + if($years>1) $str .= "$years years, "; + if($weeks==1) $str .= "$weeks week, "; + if($weeks>1) $str .= "$weeks weeks, "; + if($days==1) $str .= "$days day,"; + if($days>1) $str .= "$days days,"; + if($hours == 1) $str .= " $hours hour and"; + if($hours>1) $str .= " $hours hours and"; + if($mins == 1) $str .= " 1 minute"; + else $str .= " $mins minutes"; + return $str; +} + +// create graphics +// +function graphics_avail() { + return extension_loaded('gd'); +} +if (isset($MYREQUEST['IMG'])) +{ + if (!graphics_avail()) { + exit(0); + } + + function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) { + $r=$diameter/2; + $w=deg2rad((360+$start+($end-$start)/2)%360); + + + if (function_exists("imagefilledarc")) { + // exists only if GD 2.0.1 is avaliable + imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE); + imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE); + imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED); + } else { + imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); + imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2); + } + if ($text) { + if ($placeindex>0) { + imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); + imagestring($im,4,$diameter, $placeindex*12,$text,$color1); + + } else { + imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); + } + } + } + + function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$text,$placeindex=0) { + $r=$diameter/2; + $w=deg2rad((360+$start+($end-$start)/2)%360); + + if ($placeindex>0) { + imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); + imagestring($im,4,$diameter, $placeindex*12,$text,$color1); + + } else { + imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); + } + } + + function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') { + global $col_black; + $x1=$x+$w-1; + $y1=$y+$h-1; + + imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black); + if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2); + else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2); + imagerectangle($im, $x, $y1, $x1, $y, $color1); + if ($text) { + if ($placeindex>0) { + + if ($placeindex<16) + { + $px=5; + $py=$placeindex*12+6; + imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2); + imageline($im,$x,$y+$h/2,$px+90,$py,$color2); + imagestring($im,2,$px,$py-6,$text,$color1); + + } else { + if ($placeindex<31) { + $px=$x+40*2; + $py=($placeindex-15)*12+6; + } else { + $px=$x+40*2+100*intval(($placeindex-15)/15); + $py=($placeindex%15)*12+6; + } + imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2); + imageline($im,$x+$w,$y+$h/2,$px,$py,$color2); + imagestring($im,2,$px+2,$py-6,$text,$color1); + } + } else { + imagestring($im,4,$x+5,$y1-16,$text,$color1); + } + } + } + + + $size = GRAPH_SIZE; // image size + if ($MYREQUEST['IMG']==3) + $image = imagecreate(2*$size+150, $size+10); + else + $image = imagecreate($size+50, $size+10); + + $col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF); + $col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30); + $col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60); + $col_black = imagecolorallocate($image, 0, 0, 0); + imagecolortransparent($image,$col_white); + + switch ($MYREQUEST['IMG']) { + + case 1: + $s=$mem['num_seg']*$mem['seg_size']; + $a=$mem['avail_mem']; + $x=$y=$size/2; + $fuzz = 0.000001; + + // This block of code creates the pie chart. It is a lot more complex than you + // would expect because we try to visualize any memory fragmentation as well. + $angle_from = 0; + $string_placement=array(); + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + $free = $mem['block_lists'][$i]; + uasort($free, 'block_sort'); + foreach($free as $block) { + if($block['offset']!=$ptr) { // Used block + $angle_to = $angle_from+($block['offset']-$ptr)/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + if( ($angle_to*360) - ($angle_from*360) >= 1) { + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + $angle_from = $angle_to; + } + $angle_to = $angle_from+($block['size'])/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + if( ($angle_to*360) - ($angle_from*360) >= 1) { + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_green); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + $angle_from = $angle_to; + $ptr = $block['offset']+$block['size']; + } + if ($ptr < $mem['seg_size']) { // memory at the end + $angle_to = $angle_from + ($mem['seg_size'] - $ptr)/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + } + foreach ($string_placement as $angle) { + text_arc($image,$x,$y,$size,$angle[0]*360,$angle[1]*360,$col_black,bsize($s*($angle[1]-$angle[0]))); + } + break; + + case 2: + $s=$cache['num_hits']+$cache['num_misses']; + $a=$cache['num_hits']; + + fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); + fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); + break; + + case 3: + $s=$mem['num_seg']*$mem['seg_size']; + $a=$mem['avail_mem']; + $x=130; + $y=1; + $j=1; + + // This block of code creates the bar chart. It is a lot more complex than you + // would expect because we try to visualize any memory fragmentation as well. + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + $free = $mem['block_lists'][$i]; + uasort($free, 'block_sort'); + foreach($free as $block) { + if($block['offset']!=$ptr) { // Used block + $h=(GRAPH_SIZE-5)*($block['offset']-$ptr)/$s; + if ($h>0) { + $j++; + if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($block['offset']-$ptr),$j); + else fill_box($image,$x,$y,50,$h,$col_black,$col_red); + } + $y+=$h; + } + $h=(GRAPH_SIZE-5)*($block['size'])/$s; + if ($h>0) { + $j++; + if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_green,bsize($block['size']),$j); + else fill_box($image,$x,$y,50,$h,$col_black,$col_green); + } + $y+=$h; + $ptr = $block['offset']+$block['size']; + } + if ($ptr < $mem['seg_size']) { // memory at the end + $h = (GRAPH_SIZE-5) * ($mem['seg_size'] - $ptr) / $s; + if ($h > 0) { + fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($mem['seg_size']-$ptr),$j++); + } + } + } + break; + case 4: + $s=$cache['num_hits']+$cache['num_misses']; + $a=$cache['num_hits']; + + fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); + fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); + break; + + } + header("Content-type: image/png"); + imagepng($image); + exit; +} + +// pretty printer for byte values +// +function bsize($s) { + foreach (array('','K','M','G') as $i => $k) { + if ($s < 1024) break; + $s/=1024; + } + return sprintf("%5.1f %sBytes",$s,$k); +} + +// sortable table header in "scripts for this host" view +function sortheader($key,$name,$extra='') { + global $MYREQUEST, $MY_SELF_WO_SORT; + + if ($MYREQUEST['SORT1']==$key) { + $MYREQUEST['SORT2'] = $MYREQUEST['SORT2']=='A' ? 'D' : 'A'; + } + return "$name"; + +} + +// create menu entry +function menu_entry($ob,$title) { + global $MYREQUEST,$MY_SELF; + if ($MYREQUEST['OB']!=$ob) { + return "
  • $title
  • "; + } else if (empty($MYREQUEST['SH'])) { + return "
  • $title
  • "; + } else { + return "
  • $title
  • "; + } +} + +function put_login_link($s="Login") +{ + global $MY_SELF,$MYREQUEST,$AUTHENTICATED; + // needs ADMIN_PASSWORD to be changed! + // + if (!USE_AUTHENTICATION) { + return; + } else if (ADMIN_PASSWORD=='password') + { + print <<$s +EOB; + } else if ($AUTHENTICATED) { + print <<$s +EOB; + } +} + +function block_sort($array1, $array2) +{ + if ($array1['offset'] > $array2['offset']) { + return 1; + } else { + return -1; + } +} + + +?> + + +APC INFO <?php echo $host ?> + + + +
    +

    + +
    Opcode Cache
    +

    + +
    +
    + +
  • Refresh Data
  • +EOB; +echo + menu_entry(1,'View Host Stats'), + menu_entry(2,'System Cache Entries'); +if ($AUTHENTICATED) { + echo menu_entry(4,'Per-Directory Entries'); +} +echo + menu_entry(3,'User Cache Entries'), + menu_entry(9,'Version Check'); + +if ($AUTHENTICATED) { + echo <<Clear $cache_mode Cache +EOB; +} +echo << +EOB; + + +// CONTENT +echo << +EOB; + +// MAIN SWITCH STATEMENT + +switch ($MYREQUEST['OB']) { + + + + + +// ----------------------------------------------- +// Host Stats +// ----------------------------------------------- +case OB_HOST_STATS: + $mem_size = $mem['num_seg']*$mem['seg_size']; + $mem_avail= $mem['avail_mem']; + $mem_used = $mem_size-$mem_avail; + $seg_size = bsize($mem['seg_size']); + $req_rate = sprintf("%.2f",($cache['num_hits']+$cache['num_misses'])/($time-$cache['start_time'])); + $hit_rate = sprintf("%.2f",($cache['num_hits'])/($time-$cache['start_time'])); + $miss_rate = sprintf("%.2f",($cache['num_misses'])/($time-$cache['start_time'])); + $insert_rate = sprintf("%.2f",($cache['num_inserts'])/($time-$cache['start_time'])); + $req_rate_user = sprintf("%.2f",($cache_user['num_hits']+$cache_user['num_misses'])/($time-$cache_user['start_time'])); + $hit_rate_user = sprintf("%.2f",($cache_user['num_hits'])/($time-$cache_user['start_time'])); + $miss_rate_user = sprintf("%.2f",($cache_user['num_misses'])/($time-$cache_user['start_time'])); + $insert_rate_user = sprintf("%.2f",($cache_user['num_inserts'])/($time-$cache_user['start_time'])); + $apcversion = phpversion('apc'); + $phpversion = phpversion(); + $number_files = $cache['num_entries']; + $size_files = bsize($cache['mem_size']); + $number_vars = $cache_user['num_entries']; + $size_vars = bsize($cache_user['mem_size']); + $i=0; + echo <<< EOB +

    General Cache Information

    + + + +EOB; + + if(!empty($_SERVER['SERVER_NAME'])) + echo "\n"; + if(!empty($_SERVER['SERVER_SOFTWARE'])) + echo "\n"; + + echo << +EOB; + echo ''; + echo ''; + echo ''; + echo <<
    APC Version$apcversion
    PHP Version$phpversion
    APC Host{$_SERVER['SERVER_NAME']} $host
    Server Software{$_SERVER['SERVER_SOFTWARE']}
    Shared Memory{$mem['num_seg']} Segment(s) with $seg_size +
    ({$cache['memory_type']} memory, {$cache['locking_type']} locking) +
    Start Time',date(DATE_FORMAT,$cache['start_time']),'
    Uptime',duration($cache['start_time']),'
    File Upload Support',$cache['file_upload_progress'],'
    +
    + +

    File Cache Information

    + + + + + + + + + +
    Cached Files$number_files ($size_files)
    Hits{$cache['num_hits']}
    Misses{$cache['num_misses']}
    Request Rate (hits, misses)$req_rate cache requests/second
    Hit Rate$hit_rate cache requests/second
    Miss Rate$miss_rate cache requests/second
    Insert Rate$insert_rate cache requests/second
    Cache full count{$cache['expunges']}
    +
    + +

    User Cache Information

    + + + + + + + + + + +
    Cached Variables$number_vars ($size_vars)
    Hits{$cache_user['num_hits']}
    Misses{$cache_user['num_misses']}
    Request Rate (hits, misses)$req_rate_user cache requests/second
    Hit Rate$hit_rate_user cache requests/second
    Miss Rate$miss_rate_user cache requests/second
    Insert Rate$insert_rate_user cache requests/second
    Cache full count{$cache_user['expunges']}
    +
    + +

    Runtime Settings

    +EOB; + + $j = 0; + foreach (ini_get_all('apc') as $k => $v) { + echo "\n"; + $j = 1 - $j; + } + + if($mem['num_seg']>1 || $mem['num_seg']==1 && count($mem['block_lists'][0])>1) + $mem_note = "Memory Usage
    (multiple slices indicate fragments)"; + else + $mem_note = "Memory Usage"; + + echo <<< EOB +
    ",$k,"",str_replace(',',',
    ',$v['local_value']),"
    +
    + +

    Host Status Diagrams

    + +EOB; + $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10); + echo << + + + +EOB; + + echo + graphics_avail() ? + ''. + "". + "\n" + : "", + '', + '\n", + '\n", + '', + '', + '\n", + '\n"; + echo <<< EOB + +
    $mem_noteHits & Misses
    \"\"\"\"
     Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size)," Hits: ',$cache['num_hits'].sprintf(" (%.1f%%)",$cache['num_hits']*100/($cache['num_hits']+$cache['num_misses'])),"
     Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size)," Misses: ',$cache['num_misses'].sprintf(" (%.1f%%)",$cache['num_misses']*100/($cache['num_hits']+$cache['num_misses'])),"
    + +
    +

    Detailed Memory Usage and Fragmentation

    + + + + +EOB; + if(isset($mem['adist'])) { + foreach($mem['adist'] as $i=>$v) { + $cur = pow(2,$i); $nxt = pow(2,$i+1)-1; + if($i==0) $range = "1"; + else $range = "$cur - $nxt"; + echo "\n"; + } + } + echo <<

    +EOB; + + // Fragementation: (freeseg - 1) / total_seg + $nseg = $freeseg = $fragsize = $freetotal = 0; + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + foreach($mem['block_lists'][$i] as $block) { + if ($block['offset'] != $ptr) { + ++$nseg; + } + $ptr = $block['offset'] + $block['size']; + /* Only consider blocks <5M for the fragmentation % */ + if($block['size']<(5*1024*1024)) $fragsize+=$block['size']; + $freetotal+=$block['size']; + } + $freeseg += count($mem['block_lists'][$i]); + } + + if ($freeseg > 1) { + $frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize/$freetotal)*100,bsize($fragsize),bsize($freetotal),$freeseg); + } else { + $frag = "0%"; + } + + if (graphics_avail()) { + $size='width='.(2*GRAPH_SIZE+150).' height='.(GRAPH_SIZE+10); + echo << +EOB; + } + echo <<Fragmentation: $frag +
    $range$v
    +
    +EOB; + + break; + + +// ----------------------------------------------- +// User Cache Entries +// ----------------------------------------------- +case OB_USER_CACHE: + if (!$AUTHENTICATED) { + echo '
    You need to login to see the user values here!
     
    '; + put_login_link("Login now!"); + echo '
    '; + break; + } + $fieldname='info'; + $fieldheading='User Entry Label'; + $fieldkey='info'; + +// ----------------------------------------------- +// System Cache Entries +// ----------------------------------------------- +case OB_SYS_CACHE: + if (!isset($fieldname)) + { + $fieldname='filename'; + $fieldheading='Script Filename'; + if(ini_get("apc.stat")) $fieldkey='inode'; + else $fieldkey='filename'; + } + if (!empty($MYREQUEST['SH'])) + { + echo <<< EOB +
    + +EOB; + + $m=0; + foreach($scope_list as $j => $list) { + foreach($cache[$list] as $i => $entry) { + if (md5($entry[$fieldkey])!=$MYREQUEST['SH']) continue; + foreach($entry as $k => $value) { + if (!$AUTHENTICATED) { + // hide all path entries if not logged in + $value=preg_replace('/^.*(\\/|\\\\)/','<hidden>/',$value); + } + + if ($k == "num_hits") { + $value=sprintf("%s (%.2f%%)",$value,$value*100/$cache['num_hits']); + } + if ($k == 'deletion_time') { + if(!$entry['deletion_time']) $value = "None"; + } + echo + "", + "", + "", + ""; + $m=1-$m; + } + if($fieldkey=='info') { + echo "\n"; + } + break; + } + } + + echo <<
    AttributeValue
    ",ucwords(preg_replace("/_/"," ",$k)),"",(preg_match("/time/",$k) && $value!='None') ? date(DATE_FORMAT,$value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),"
    Stored Value
    ";
    +					$output = var_export(apc_fetch($entry[$fieldkey]),true);
    +					echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
    +					echo "
    +
    +EOB; + break; + } + + $cols=6; + echo <<
    Scope: + + ", + ", Sorting:', + '', + '', + '  Search: ', + ' ', + '
    '; + + if (isset($MYREQUEST['SEARCH'])) { + // Don't use preg_quote because we want the user to be able to specify a + // regular expression subpattern. + $MYREQUEST['SEARCH'] = '/'.str_replace('/', '\\/', $MYREQUEST['SEARCH']).'/i'; + if (preg_match($MYREQUEST['SEARCH'], 'test') === false) { + echo '
    Error: enter a valid regular expression as a search query.
    '; + break; + } + } + + echo + '
    ', + '', + '', + '', + '', + '', + '', + ''; + + if($fieldname=='info') { + $cols+=2; + echo ''; + } + echo ''; + + // builds list with alpha numeric sortable keys + // + $list = array(); + foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $i => $entry) { + switch($MYREQUEST['SORT1']) { + case 'A': $k=sprintf('%015d-',$entry['access_time']); break; + case 'H': $k=sprintf('%015d-',$entry['num_hits']); break; + case 'Z': $k=sprintf('%015d-',$entry['mem_size']); break; + case 'M': $k=sprintf('%015d-',$entry['mtime']); break; + case 'C': $k=sprintf('%015d-',$entry['creation_time']); break; + case 'T': $k=sprintf('%015d-',$entry['ttl']); break; + case 'D': $k=sprintf('%015d-',$entry['deletion_time']); break; + case 'S': $k=''; break; + } + if (!$AUTHENTICATED) { + // hide all path entries if not logged in + $list[$k.$entry[$fieldname]]=preg_replace('/^.*(\\/|\\\\)/','*hidden*/',$entry); + } else { + $list[$k.$entry[$fieldname]]=$entry; + } + } + + if ($list) { + + // sort list + // + switch ($MYREQUEST['SORT2']) { + case "A": krsort($list); break; + case "D": ksort($list); break; + } + + // output list + $i=0; + foreach($list as $k => $entry) { + if(!$MYREQUEST['SEARCH'] || preg_match($MYREQUEST['SEARCH'], $entry[$fieldname]) != 0) { + $field_value = htmlentities(strip_tags($entry[$fieldname],''), ENT_QUOTES, 'UTF-8'); + echo + '', + "', + '', + '', + '', + '', + ''; + + if($fieldname=='info') { + if($entry['ttl']) + echo ''; + else + echo ''; + } + if ($entry['deletion_time']) { + + echo ''; + } else if ($MYREQUEST['OB'] == OB_USER_CACHE) { + + echo ''; + } else { + echo ''; + } + echo ''; + $i++; + if ($i == $MYREQUEST['COUNT']) + break; + } + } + + } else { + echo ''; + } + echo <<< EOB +
    ',sortheader('S',$fieldheading, "&OB=".$MYREQUEST['OB']),'',sortheader('H','Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Size', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Last accessed',"&OB=".$MYREQUEST['OB']),'',sortheader('M','Last modified',"&OB=".$MYREQUEST['OB']),'',sortheader('C','Created at', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Timeout',"&OB=".$MYREQUEST['OB']),'',sortheader('D','Deleted at',"&OB=".$MYREQUEST['OB']),'
    ",$field_value,'',$entry['num_hits'],'',$entry['mem_size'],'',date(DATE_FORMAT,$entry['access_time']),'',date(DATE_FORMAT,$entry['mtime']),'',date(DATE_FORMAT,$entry['creation_time']),''.$entry['ttl'].' secondsNone', date(DATE_FORMAT,$entry['deletion_time']), ''; + echo '[Delete Now]'; + echo '  
    No data
    +EOB; + + if ($list && $i < count($list)) { + echo "",count($list)-$i,' more available...'; + } + + echo <<< EOB +
    +EOB; + break; + + +// ----------------------------------------------- +// Per-Directory System Cache Entries +// ----------------------------------------------- +case OB_SYS_CACHE_DIR: + if (!$AUTHENTICATED) { + break; + } + + echo <<
    Scope: + + ", + ", Sorting:', + '', + '', + ", Group By Dir Level:', + ' ', + '
    ', + + '
    ', + '', + '', + '', + '', + '', + '', + '', + ''; + + // builds list with alpha numeric sortable keys + // + $tmp = $list = array(); + foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $entry) { + $n = dirname($entry['filename']); + if ($MYREQUEST['AGGR'] > 0) { + $n = preg_replace("!^(/?(?:[^/\\\\]+[/\\\\]){".($MYREQUEST['AGGR']-1)."}[^/\\\\]*).*!", "$1", $n); + } + if (!isset($tmp[$n])) { + $tmp[$n] = array('hits'=>0,'size'=>0,'ents'=>0); + } + $tmp[$n]['hits'] += $entry['num_hits']; + $tmp[$n]['size'] += $entry['mem_size']; + ++$tmp[$n]['ents']; + } + + foreach ($tmp as $k => $v) { + switch($MYREQUEST['SORT1']) { + case 'A': $kn=sprintf('%015d-',$v['size'] / $v['ents']);break; + case 'T': $kn=sprintf('%015d-',$v['ents']); break; + case 'H': $kn=sprintf('%015d-',$v['hits']); break; + case 'Z': $kn=sprintf('%015d-',$v['size']); break; + case 'C': $kn=sprintf('%015d-',$v['hits'] / $v['ents']);break; + case 'S': $kn = $k; break; + } + $list[$kn.$k] = array($k, $v['ents'], $v['hits'], $v['size']); + } + + if ($list) { + + // sort list + // + switch ($MYREQUEST['SORT2']) { + case "A": krsort($list); break; + case "D": ksort($list); break; + } + + // output list + $i = 0; + foreach($list as $entry) { + echo + '', + "', + '', + '', + '', + '', + '', + ''; + + if (++$i == $MYREQUEST['COUNT']) break; + } + + } else { + echo ''; + } + echo <<< EOB +
    ',sortheader('S','Directory Name', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Number of Files',"&OB=".$MYREQUEST['OB']),'',sortheader('H','Total Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Total Size', "&OB=".$MYREQUEST['OB']),'',sortheader('C','Avg. Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Avg. Size', "&OB=".$MYREQUEST['OB']),'
    ",$entry[0],'',$entry[1],'',$entry[2],'',$entry[3],'',round($entry[2] / $entry[1]),'',round($entry[3] / $entry[1]),'
    No data
    +EOB; + + if ($list && $i < count($list)) { + echo "",count($list)-$i,' more available...'; + } + + echo <<< EOB +
    +EOB; + break; + +// ----------------------------------------------- +// Version check +// ----------------------------------------------- +case OB_VERSION_CHECK: + echo <<

    APC Version Information

    + + + + +EOB; + if (defined('PROXY')) { + $ctxt = stream_context_create( array( 'http' => array( 'proxy' => PROXY, 'request_fulluri' => True ) ) ); + $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss", False, $ctxt); + } else { + $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss"); + } + if (!$rss) { + echo ''; + } else { + $apcversion = phpversion('apc'); + + preg_match('!APC ([0-9.]+)!', $rss, $match); + echo ''; + echo ''; + } + echo <<< EOB +
    Unable to fetch version information.
    '; + if (version_compare($apcversion, $match[1], '>=')) { + echo '
    You are running the latest version of APC ('.$apcversion.')
    '; + $i = 3; + } else { + echo '
    You are running an older version of APC ('.$apcversion.'), + newer version '.$match[1].' is available at + http://pecl.php.net/package/APC/'.$match[1].' +
    '; + $i = -1; + } + echo '

    Change Log:


    '; + + preg_match_all('!<(title|description)>([^<]+)!', $rss, $match); + next($match[2]); next($match[2]); + + while (list(,$v) = each($match[2])) { + list(,$ver) = explode(' ', $v, 2); + if ($i < 0 && version_compare($apcversion, $ver, '>=')) { + break; + } else if (!$i--) { + break; + } + echo "".htmlspecialchars($v, ENT_QUOTES, 'UTF-8')."
    "; + echo nl2br(htmlspecialchars(current($match[2]), ENT_QUOTES, 'UTF-8'))."
    "; + next($match[2]); + } + echo '
    + +EOB; + break; + +} + +echo <<< EOB + +EOB; + +?> + + + + --- /dev/null +++ b/ext/apc/apc_php.h @@ -0,0 +1,77 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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: apc_php.h 300979 2010-07-04 10:15:05Z kalle $ */ + +#ifndef APC_PHP_H +#define APC_PHP_H + +/* + * The purpose of this header file is to include all PHP and Zend headers that + * are typically needed elsewhere in APC. This makes it easy to insure that + * all required headers are available. + */ + +#include "php.h" +#include "zend.h" +#include "zend_API.h" +#include "zend_compile.h" +#include "zend_hash.h" +#include "zend_extensions.h" + +#if ZEND_MODULE_API_NO >= 20100409 +#define ZEND_ENGINE_2_4 +#endif +#if ZEND_MODULE_API_NO > 20060613 +#define ZEND_ENGINE_2_3 +#endif +#if ZEND_MODULE_API_NO > 20050922 +#define ZEND_ENGINE_2_2 +#endif +#if ZEND_MODULE_API_NO > 20050921 +#define ZEND_ENGINE_2_1 +#endif +#ifdef ZEND_ENGINE_2_1 +#include "zend_vm.h" +#endif + +#include "rfc1867.h" + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_php_pcre.h @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | APC/PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2010 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. | + +----------------------------------------------------------------------+ + | Author: Andrei Zmievski | + +----------------------------------------------------------------------+ + */ + +/* $Id: apc_php_pcre.h 300979 2010-07-04 10:15:05Z kalle $ */ + +#ifndef PHP_PCRE_H +#define PHP_PCRE_H + +#if HAVE_PCRE || HAVE_BUNDLED_PCRE + +#if HAVE_BUNDLED_PCRE +#include "ext/pcre/pcrelib/pcre.h" +#else +#include "pcre.h" +#endif + +#if HAVE_LOCALE_H +#include +#endif + +PHP_FUNCTION(preg_match); +PHP_FUNCTION(preg_match_all); +PHP_FUNCTION(preg_replace); +PHP_FUNCTION(preg_replace_callback); +PHP_FUNCTION(preg_split); +PHP_FUNCTION(preg_quote); +PHP_FUNCTION(preg_grep); + +PHPAPI char *php_pcre_replace(char *regex, int regex_len, char *subject, int subject_len, zval *replace_val, int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC); +PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *options TSRMLS_DC); +PHPAPI pcre* pcre_get_compiled_regex_ex(char *regex, pcre_extra **extra, int *preg_options, int *coptions TSRMLS_DC); + +extern zend_module_entry pcre_module_entry; +#define pcre_module_ptr &pcre_module_entry + +typedef struct { + pcre *re; + pcre_extra *extra; + int preg_options; +#if HAVE_SETLOCALE + char *locale; + unsigned const char *tables; +#endif + int compile_options; + int refcount; +} pcre_cache_entry; + +PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_len TSRMLS_DC); + +PHPAPI void php_pcre_match_impl( pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value, + zval *subpats, int global, int use_flags, long flags, long start_offset TSRMLS_DC); + +PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value, + int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC); + +PHPAPI void php_pcre_split_impl( pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value, + long limit_val, long flags TSRMLS_DC); + +PHPAPI void php_pcre_grep_impl( pcre_cache_entry *pce, zval *input, zval *return_value, + long flags TSRMLS_DC); + +ZEND_BEGIN_MODULE_GLOBALS(pcre) + HashTable pcre_cache; + long backtrack_limit; + long recursion_limit; + int error_code; +ZEND_END_MODULE_GLOBALS(pcre) + +#ifdef ZTS +# define PCRE_G(v) TSRMG(pcre_globals_id, zend_pcre_globals *, v) +#else +# define PCRE_G(v) (pcre_globals.v) +#endif + +#else + +#define pcre_module_ptr NULL + +#endif /* HAVE_PCRE || HAVE_BUNDLED_PCRE */ + +#define phpext_pcre_ptr pcre_module_ptr + +#endif /* PHP_PCRE_H */ --- /dev/null +++ b/ext/apc/apc_pool.c @@ -0,0 +1,488 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Yahoo! Inc. in 2008. + + Future revisions and derivatives of this source code must acknowledge + Yahoo! 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: apc_pool.c 301964 2010-08-07 03:34:18Z rasmus $ */ + + +#include "apc_pool.h" +#include + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + + +/* {{{ forward references */ +static apc_pool* apc_unpool_create(apc_pool_type type, apc_malloc_t, apc_free_t, apc_protect_t, apc_unprotect_t TSRMLS_DC); +static apc_pool* apc_realpool_create(apc_pool_type type, apc_malloc_t, apc_free_t, apc_protect_t, apc_unprotect_t TSRMLS_DC); +/* }}} */ + +/* {{{ apc_pool_create */ +apc_pool* apc_pool_create(apc_pool_type pool_type, + apc_malloc_t allocate, + apc_free_t deallocate, + apc_protect_t protect, + apc_unprotect_t unprotect + TSRMLS_DC) +{ + if(pool_type == APC_UNPOOL) { + return apc_unpool_create(pool_type, allocate, deallocate, + protect, unprotect TSRMLS_CC); + } + + return apc_realpool_create(pool_type, allocate, deallocate, + protect, unprotect TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_pool_destroy */ +void apc_pool_destroy(apc_pool *pool TSRMLS_DC) +{ + apc_free_t deallocate = pool->deallocate; + apc_pcleanup_t cleanup = pool->cleanup; + + cleanup(pool TSRMLS_CC); + deallocate(pool TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_unpool implementation */ + +typedef struct _apc_unpool apc_unpool; + +struct _apc_unpool { + apc_pool parent; + /* apc_unpool is a lie! */ +}; + +static void* apc_unpool_alloc(apc_pool* pool, size_t size TSRMLS_DC) +{ + apc_unpool *upool = (apc_unpool*)pool; + + apc_malloc_t allocate = upool->parent.allocate; + + upool->parent.size += size; + upool->parent.used += size; + + return allocate(size TSRMLS_CC); +} + +static void apc_unpool_free(apc_pool* pool, void *ptr TSRMLS_DC) +{ + apc_unpool *upool = (apc_unpool*) pool; + + apc_free_t deallocate = upool->parent.deallocate; + + deallocate(ptr TSRMLS_CC); +} + +static void apc_unpool_cleanup(apc_pool* pool TSRMLS_DC) +{ +} + +static apc_pool* apc_unpool_create(apc_pool_type type, + apc_malloc_t allocate, apc_free_t deallocate, + apc_protect_t protect, apc_unprotect_t unprotect + TSRMLS_DC) +{ + apc_unpool* upool = allocate(sizeof(apc_unpool) TSRMLS_CC); + + if (!upool) { + return NULL; + } + + upool->parent.type = type; + upool->parent.allocate = allocate; + upool->parent.deallocate = deallocate; + + upool->parent.protect = protect; + upool->parent.unprotect = unprotect; + + upool->parent.palloc = apc_unpool_alloc; + upool->parent.pfree = apc_unpool_free; + + upool->parent.cleanup = apc_unpool_cleanup; + + upool->parent.used = 0; + upool->parent.size = 0; + + return &(upool->parent); +} +/* }}} */ + + +/*{{{ apc_realpool implementation */ + +/* {{{ typedefs */ +typedef struct _pool_block +{ + size_t avail; + size_t capacity; + unsigned char *mark; + struct _pool_block *next; + unsigned :0; /* this should align to word */ + /* data comes here */ +}pool_block; + +/* + parts in ? are optional and turned on for fun, memory loss, + and for something else that I forgot about ... ah, debugging + + |--------> data[] |<-- non word boundary (too) + +-------------+--------------+-----------+-------------+-------------->>> + | pool_block | ?sizeinfo<1> | block<1> | ?redzone<1> | ?sizeinfo<2> + | | (size_t) | | padded left | + +-------------+--------------+-----------+-------------+-------------->>> + */ + +typedef struct _apc_realpool apc_realpool; + +struct _apc_realpool +{ + struct _apc_pool parent; + + size_t dsize; + void *owner; + + pool_block *head; + pool_block first; +}; + +/* }}} */ + +/* {{{ redzone code */ +static const unsigned char decaff[] = { + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad +}; + +/* a redzone is at least 4 (0xde,0xca,0xc0,0xff) bytes */ +#define REDZONE_SIZE(size) \ + ((ALIGNWORD((size)) > ((size) + 4)) ? \ + (ALIGNWORD((size)) - (size)) : /* does not change realsize */\ + ALIGNWORD((size)) - (size) + ALIGNWORD((sizeof(char)))) /* adds 1 word to realsize */ + +#define SIZEINFO_SIZE ALIGNWORD(sizeof(size_t)) + +#define MARK_REDZONE(block, redsize) do {\ + memcpy(block, decaff, redsize );\ + } while(0) + +#define CHECK_REDZONE(block, redsize) (memcmp(block, decaff, redsize) == 0) + +/* }}} */ + +#define INIT_POOL_BLOCK(rpool, entry, size) do {\ + (entry)->avail = (entry)->capacity = (size);\ + (entry)->mark = ((unsigned char*)(entry)) + ALIGNWORD(sizeof(pool_block));\ + (entry)->next = (rpool)->head;\ + (rpool)->head = (entry);\ +} while(0) + +/* {{{ create_pool_block */ +static pool_block* create_pool_block(apc_realpool *rpool, size_t size TSRMLS_DC) +{ + apc_malloc_t allocate = rpool->parent.allocate; + + size_t realsize = sizeof(pool_block) + ALIGNWORD(size); + + pool_block* entry = allocate(realsize TSRMLS_CC); + + if (!entry) { + return NULL; + } + + INIT_POOL_BLOCK(rpool, entry, size); + + rpool->parent.size += realsize; + + return entry; +} +/* }}} */ + +/* {{{ apc_realpool_alloc */ +static void* apc_realpool_alloc(apc_pool *pool, size_t size TSRMLS_DC) +{ + apc_realpool *rpool = (apc_realpool*)pool; + unsigned char *p = NULL; + size_t realsize = ALIGNWORD(size); + size_t poolsize; + unsigned char *redzone = NULL; + size_t redsize = 0; + size_t *sizeinfo= NULL; + pool_block *entry = NULL; + + if(APC_POOL_HAS_REDZONES(pool)) { + redsize = REDZONE_SIZE(size); /* redsize might be re-using word size padding */ + realsize = size + redsize; /* recalculating realsize */ + } else { + redsize = realsize - size; /* use padding space */ + } + + if(APC_POOL_HAS_SIZEINFO(pool)) { + realsize += ALIGNWORD(sizeof(size_t)); + } + + + for(entry = rpool->head; entry != NULL; entry = entry->next) { + if(entry->avail >= realsize) { + goto found; + } + } + + poolsize = ALIGNSIZE(realsize, rpool->dsize); + + entry = create_pool_block(rpool, poolsize TSRMLS_CC); + + if(!entry) { + return NULL; + } + +found: + p = entry->mark; + + if(APC_POOL_HAS_SIZEINFO(pool)) { + sizeinfo = (size_t*)p; + p += SIZEINFO_SIZE; + *sizeinfo = size; + } + + redzone = p + size; + + if(APC_POOL_HAS_REDZONES(pool)) { + MARK_REDZONE(redzone, redsize); + } + +#ifdef VALGRIND_MAKE_MEM_NOACCESS + if(redsize != 0) { + VALGRIND_MAKE_MEM_NOACCESS(redzone, redsize); + } +#endif + + entry->avail -= realsize; + entry->mark += realsize; + pool->used += realsize; + +#ifdef VALGRIND_MAKE_MEM_UNDEFINED + /* need to write before reading data off this */ + VALGRIND_MAKE_MEM_UNDEFINED(p, size); +#endif + + return (void*)p; +} +/* }}} */ + +/* {{{ apc_realpool_check_integrity */ +/* + * Checking integrity at runtime, does an + * overwrite check only when the sizeinfo + * is set. + */ +static int apc_realpool_check_integrity(apc_realpool *rpool) +{ + apc_pool *pool = &(rpool->parent); + pool_block *entry; + size_t *sizeinfo = NULL; + unsigned char *start; + size_t realsize; + unsigned char *redzone; + size_t redsize; + + for(entry = rpool->head; entry != NULL; entry = entry->next) { + start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block)); + if((entry->mark - start) != (entry->capacity - entry->avail)) { + return 0; + } + } + + if(!APC_POOL_HAS_REDZONES(pool) || + !APC_POOL_HAS_SIZEINFO(pool)) { + return 1; + } + + for(entry = rpool->head; entry != NULL; entry = entry->next) { + start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block)); + + while(start < entry->mark) { + sizeinfo = (size_t*)start; + /* redzone starts where real data ends, in a non-word boundary + * redsize is at least 4 bytes + whatever's needed to make it + * to another word boundary. + */ + redzone = start + SIZEINFO_SIZE + (*sizeinfo); + redsize = REDZONE_SIZE(*sizeinfo); +#ifdef VALGRIND_MAKE_MEM_DEFINED + VALGRIND_MAKE_MEM_DEFINED(redzone, redsize); +#endif + if(!CHECK_REDZONE(redzone, redsize)) + { + /* + fprintf(stderr, "Redzone check failed for %p\n", + start + ALIGNWORD(sizeof(size_t)));*/ + return 0; + } +#ifdef VALGRIND_MAKE_MEM_NOACCESS + VALGRIND_MAKE_MEM_NOACCESS(redzone, redsize); +#endif + realsize = SIZEINFO_SIZE + *sizeinfo + redsize; + start += realsize; + } + } + + return 1; +} +/* }}} */ + +/* {{{ apc_realpool_free */ +/* + * free does not do anything other than + * check for redzone values when free'ing + * data areas. + */ +static void apc_realpool_free(apc_pool *pool, void *p TSRMLS_DC) +{ +} +/* }}} */ + +static void apc_realpool_cleanup(apc_pool *pool TSRMLS_DC) +{ + pool_block *entry; + pool_block *tmp; + apc_realpool *rpool = (apc_realpool*)pool; + apc_free_t deallocate = pool->deallocate; + + assert(apc_realpool_check_integrity(rpool)!=0); + + entry = rpool->head; + + while(entry->next != NULL) { + tmp = entry->next; + deallocate(entry TSRMLS_CC); + entry = tmp; + } +} + +/* {{{ apc_realpool_create */ +static apc_pool* apc_realpool_create(apc_pool_type type, apc_malloc_t allocate, apc_free_t deallocate, + apc_protect_t protect, apc_unprotect_t unprotect + TSRMLS_DC) +{ + + size_t dsize = 0; + apc_realpool *rpool; + + switch(type & APC_POOL_SIZE_MASK) { + case APC_SMALL_POOL: + dsize = 512; + break; + + case APC_LARGE_POOL: + dsize = 8192; + break; + + case APC_MEDIUM_POOL: + dsize = 4096; + break; + + default: + return NULL; + } + + rpool = (apc_realpool*)allocate((sizeof(apc_realpool) + ALIGNWORD(dsize)) TSRMLS_CC); + + if(!rpool) { + return NULL; + } + + rpool->parent.type = type; + + rpool->parent.allocate = allocate; + rpool->parent.deallocate = deallocate; + + rpool->parent.size = sizeof(apc_realpool) + ALIGNWORD(dsize); + + rpool->parent.palloc = apc_realpool_alloc; + rpool->parent.pfree = apc_realpool_free; + + rpool->parent.protect = protect; + rpool->parent.unprotect = unprotect; + + rpool->parent.cleanup = apc_realpool_cleanup; + + rpool->dsize = dsize; + rpool->head = NULL; + + INIT_POOL_BLOCK(rpool, &(rpool->first), dsize); + + return &(rpool->parent); +} + + +/* }}} */ + +/* {{{ apc_pool_init */ +void apc_pool_init() +{ + /* put all ye sanity checks here */ + assert(sizeof(decaff) > REDZONE_SIZE(ALIGNWORD(sizeof(char)))); + assert(sizeof(pool_block) == ALIGNWORD(sizeof(pool_block))); +#if APC_POOL_DEBUG + assert((APC_POOL_SIZE_MASK & (APC_POOL_SIZEINFO | APC_POOL_REDZONES)) == 0); +#endif +} +/* }}} */ + +/* {{{ apc_pstrdup */ +void* apc_pstrdup(const char* s, apc_pool* pool TSRMLS_DC) +{ + return s != NULL ? apc_pmemcpy(s, (strlen(s) + 1), pool TSRMLS_CC) : NULL; +} +/* }}} */ + +/* {{{ apc_pmemcpy */ +void* apc_pmemcpy(const void* p, size_t n, apc_pool* pool TSRMLS_DC) +{ + void* q; + + if (p != NULL && (q = apc_pool_alloc(pool, n)) != NULL) { + memcpy(q, p, n); + return q; + } + return NULL; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_pool.h @@ -0,0 +1,114 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Yahoo! Inc. in 2008. + + Future revisions and derivatives of this source code must acknowledge + Yahoo! 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: apc_pool.h 301682 2010-07-29 11:09:00Z gopalv $ */ + +#ifndef APC_POOL_H +#define APC_POOL_H + +#include "apc.h" +#include "apc_sma.h" + +/* #define APC_POOL_DEBUG 1 */ + +typedef enum { + APC_UNPOOL = 0x0, + APC_SMALL_POOL = 0x1, + APC_MEDIUM_POOL = 0x2, + APC_LARGE_POOL = 0x3, + APC_POOL_SIZE_MASK = 0x7, /* waste a bit */ +#if APC_POOL_DEBUG + APC_POOL_REDZONES = 0x08, + APC_POOL_SIZEINFO = 0x10, + APC_POOL_OPT_MASK = 0x18 +#endif +} apc_pool_type; + +#if APC_POOL_DEBUG +#define APC_POOL_HAS_SIZEINFO(pool) ((pool->type & APC_POOL_SIZEINFO)!=0) +#define APC_POOL_HAS_REDZONES(pool) ((pool->type & APC_POOL_REDZONES)!=0) +#else +/* let gcc optimize away the optional features */ +#define APC_POOL_HAS_SIZEINFO(pool) (0) +#define APC_POOL_HAS_REDZONES(pool) (0) +#endif + + +typedef struct _apc_pool apc_pool; + +typedef void (*apc_pcleanup_t)(apc_pool *pool TSRMLS_DC); + +typedef void* (*apc_palloc_t)(apc_pool *pool, size_t size TSRMLS_DC); +typedef void (*apc_pfree_t) (apc_pool *pool, void* p TSRMLS_DC); + +typedef void* (*apc_protect_t) (void *p); +typedef void* (*apc_unprotect_t)(void *p); + +struct _apc_pool { + apc_pool_type type; + + apc_malloc_t allocate; + apc_free_t deallocate; + + apc_palloc_t palloc; + apc_pfree_t pfree; + + apc_protect_t protect; + apc_unprotect_t unprotect; + + apc_pcleanup_t cleanup; + + size_t size; + size_t used; + + /* apc_realpool and apc_unpool add more here */ +}; + +#define apc_pool_alloc(pool, size) ((void *) pool->palloc(pool, size TSRMLS_CC)) +#define apc_pool_free(pool, ptr) ((void) pool->pfree (pool, ptr TSRMLS_CC)) + +#define apc_pool_protect(pool, ptr) (pool->protect ? \ + (pool)->protect((ptr)) : (ptr)) + +#define apc_pool_unprotect(pool, ptr) (pool->unprotect ? \ + (pool)->unprotect((ptr)) : (ptr)) + +extern void apc_pool_init(); + +extern apc_pool* apc_pool_create(apc_pool_type pool_type, + apc_malloc_t allocate, + apc_free_t deallocate, + apc_protect_t protect, + apc_unprotect_t unprotect + TSRMLS_DC); + +extern void apc_pool_destroy(apc_pool* pool TSRMLS_DC); + +extern void* apc_pmemcpy(const void* p, size_t n, apc_pool* pool TSRMLS_DC); +extern void* apc_pstrdup(const char* s, apc_pool* pool TSRMLS_DC); + +#endif --- /dev/null +++ b/ext/apc/apc_pthreadmutex.c @@ -0,0 +1,111 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_pthreadmutex.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc_pthreadmutex.h" + +#ifdef APC_PTHREADMUTEX_LOCKS + +pthread_mutex_t *apc_pthreadmutex_create(pthread_mutex_t *lock TSRMLS_DC) +{ + int result; + pthread_mutexattr_t* attr; + attr = malloc(sizeof(pthread_mutexattr_t)); + + result = pthread_mutexattr_init(attr); + if(result == ENOMEM) { + apc_error("pthread mutex error: Insufficient memory exists to create the mutex attribute object." TSRMLS_CC); + } else if(result == EINVAL) { + apc_error("pthread mutex error: attr does not point to writeable memory." TSRMLS_CC); + } else if(result == EFAULT) { + apc_error("pthread mutex error: attr is an invalid pointer." TSRMLS_CC); + } + +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + result = pthread_mutexattr_settype(attr, PTHREAD_MUTEX_ADAPTIVE_NP); + if (result == EINVAL) { + apc_error("pthread_mutexattr_settype: unable to set adaptive mutexes" TSRMLS_CC); + } +#endif + + /* pthread_mutexattr_settype(attr, PTHREAD_MUTEX_ERRORCHECK); */ + result = pthread_mutexattr_setpshared(attr, PTHREAD_PROCESS_SHARED); + if(result == EINVAL) { + apc_error("pthread mutex error: attr is not an initialized mutex attribute object, or pshared is not a valid process-shared state setting." TSRMLS_CC); + } else if(result == EFAULT) { + apc_error("pthread mutex error: attr is an invalid pointer." TSRMLS_CC); + } else if(result == ENOTSUP) { + apc_error("pthread mutex error: pshared was set to PTHREAD_PROCESS_SHARED." TSRMLS_CC); + } + + if(pthread_mutex_init(lock, attr)) { + apc_error("unable to initialize pthread lock" TSRMLS_CC); + } + return lock; +} + +void apc_pthreadmutex_destroy(pthread_mutex_t *lock) +{ + return; /* we don't actually destroy the mutex, as it would destroy it for all processes */ +} + +void apc_pthreadmutex_lock(pthread_mutex_t *lock TSRMLS_DC) +{ + int result; + result = pthread_mutex_lock(lock); + if(result == EINVAL) { + apc_error("unable to obtain pthread lock (EINVAL)" TSRMLS_CC); + } else if(result == EDEADLK) { + apc_error("unable to obtain pthread lock (EDEADLK)" TSRMLS_CC); + } +} + +void apc_pthreadmutex_unlock(pthread_mutex_t *lock TSRMLS_DC) +{ + if(pthread_mutex_unlock(lock)) { + apc_error("unable to unlock pthread lock" TSRMLS_CC); + } +} + +zend_bool apc_pthreadmutex_nonblocking_lock(pthread_mutex_t *lock TSRMLS_DC) +{ + int rval; + rval = pthread_mutex_trylock(lock); + if(rval == EBUSY) { /* Lock is already held */ + return 0; + } else if(rval == 0) { /* Obtained lock */ + return 1; + } else { /* Other error */ + apc_error("unable to obtain pthread trylock" TSRMLS_CC); + return 0; + } +} + + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_pthreadmutex.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_pthreadmutex.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_PTHREADMUTEX_H +#define APC_PTHREADMUTEX_H + +#include "apc.h" + +#ifdef APC_PTHREADMUTEX_LOCKS + +#include + +pthread_mutex_t *apc_pthreadmutex_create(pthread_mutex_t *lock TSRMLS_DC); +void apc_pthreadmutex_destroy(pthread_mutex_t *lock); +void apc_pthreadmutex_lock(pthread_mutex_t *lock TSRMLS_DC); +void apc_pthreadmutex_unlock(pthread_mutex_t *lock TSRMLS_DC); +zend_bool apc_pthreadmutex_nonblocking_lock(pthread_mutex_t *lock TSRMLS_DC); + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_rfc1867.c @@ -0,0 +1,232 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_rfc1867.c 305798 2010-11-28 13:43:20Z gopalv $*/ + +#include "apc.h" +#include "apc_globals.h" +#include "rfc1867.h" + +#ifdef PHP_WIN32 +#include "win32/time.h" +#endif + +#ifdef MULTIPART_EVENT_FORMDATA +extern int _apc_store(char *strkey, int strkey_len, const zval *val, const uint ttl, const int exclusive TSRMLS_DC); +extern int _apc_update(char *strkey, int strkey_len, apc_cache_updater_t updater, void* data TSRMLS_DC); + +static int update_bytes_processed(apc_cache_t* cache, apc_cache_entry_t* entry, void* data) { + int *bytes_ptr = (int*)data; + zval* val = entry->data.user.val; + + if(Z_TYPE_P(val) == IS_ARRAY) { + HashTable *ht = val->value.ht; + Bucket* curr = NULL; + for (curr = ht->pListHead; curr != NULL; curr = curr->pListNext) { + if(curr->nKeyLength == 8 && + (!memcmp(curr->arKey, "current", curr->nKeyLength))) { + zval* current = ((zval**)curr->pData)[0]; + current->value.lval = *bytes_ptr; + return 1; + } + } + } + + return 0; +} + +static double my_time() { + struct timeval a; + double t; + gettimeofday(&a, NULL); + t = a.tv_sec + (a.tv_usec/1000000.00); + return t; +} + + +#define RFC1867_DATA(name) \ + ((request_data)->name) + +int apc_rfc1867_progress(uint event, void *event_data, void **extra TSRMLS_DC) { + apc_rfc1867_data *request_data = &APCG(rfc1867_data); + zval *track = NULL; + + switch (event) { + case MULTIPART_EVENT_START: + { + multipart_event_start *data = (multipart_event_start *) event_data; + + RFC1867_DATA(content_length) = data->content_length; + RFC1867_DATA(tracking_key)[0] = '\0'; + RFC1867_DATA(name)[0] = '\0'; + RFC1867_DATA(cancel_upload) = 0; + RFC1867_DATA(temp_filename) = NULL; + RFC1867_DATA(filename)[0] = '\0'; + RFC1867_DATA(key_length) = 0; + RFC1867_DATA(start_time) = my_time(); + RFC1867_DATA(bytes_processed) = 0; + RFC1867_DATA(prev_bytes_processed) = 0; + RFC1867_DATA(rate) = 0; + RFC1867_DATA(update_freq) = (int) APCG(rfc1867_freq); + RFC1867_DATA(started) = 0; + + if(RFC1867_DATA(update_freq) < 0) { // frequency is a percentage, not bytes + RFC1867_DATA(update_freq) = (int) (RFC1867_DATA(content_length) * APCG(rfc1867_freq) / 100); + } + } + break; + + case MULTIPART_EVENT_FORMDATA: + { + int prefix_len = strlen(APCG(rfc1867_prefix)); + multipart_event_formdata *data = (multipart_event_formdata *) event_data; + if(data->name && !strncasecmp(data->name, APCG(rfc1867_name), strlen(APCG(rfc1867_name))) + && data->value && data->length) { + + if(data->length >= sizeof(RFC1867_DATA(tracking_key)) - prefix_len) { + apc_warning("Key too long for '%s'. Maximum size is '%d' characters." TSRMLS_CC, + APCG(rfc1867_name), + sizeof(RFC1867_DATA(tracking_key)) - prefix_len); + break; + } + + if(RFC1867_DATA(started)) { + apc_warning("Upload progress key '%s' should be before the file upload entry in the form." TSRMLS_CC, + APCG(rfc1867_name)); + break; + } + + strlcat(RFC1867_DATA(tracking_key), APCG(rfc1867_prefix), 63); + strlcat(RFC1867_DATA(tracking_key), *data->value, 63); + RFC1867_DATA(key_length) = data->length + prefix_len; + RFC1867_DATA(bytes_processed) = data->post_bytes_processed; + } + } + break; + + case MULTIPART_EVENT_FILE_START: + { + RFC1867_DATA(started) = 1; + if(*RFC1867_DATA(tracking_key)) { + multipart_event_file_start *data = (multipart_event_file_start *) event_data; + + RFC1867_DATA(bytes_processed) = data->post_bytes_processed; + strlcpy(RFC1867_DATA(filename),*data->filename,128); + RFC1867_DATA(temp_filename) = NULL; + strlcpy(RFC1867_DATA(name),data->name,64); + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", RFC1867_DATA(content_length)); + add_assoc_long(track, "current", RFC1867_DATA(bytes_processed)); + add_assoc_string(track, "filename", RFC1867_DATA(filename), 1); + add_assoc_string(track, "name", RFC1867_DATA(name), 1); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", RFC1867_DATA(start_time)); + _apc_store(RFC1867_DATA(tracking_key), RFC1867_DATA(key_length)+1, track, APCG(rfc1867_ttl), 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + } + break; + + case MULTIPART_EVENT_FILE_DATA: + if(*RFC1867_DATA(tracking_key)) { + multipart_event_file_data *data = (multipart_event_file_data *) event_data; + RFC1867_DATA(bytes_processed) = data->post_bytes_processed; + if(RFC1867_DATA(bytes_processed) - RFC1867_DATA(prev_bytes_processed) > (uint) RFC1867_DATA(update_freq)) { + if(!_apc_update(RFC1867_DATA(tracking_key), RFC1867_DATA(key_length), update_bytes_processed, &RFC1867_DATA(bytes_processed) TSRMLS_CC)) { + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", RFC1867_DATA(content_length)); + add_assoc_long(track, "current", RFC1867_DATA(bytes_processed)); + add_assoc_string(track, "filename", RFC1867_DATA(filename), 1); + add_assoc_string(track, "name", RFC1867_DATA(name), 1); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", RFC1867_DATA(start_time)); + _apc_store(RFC1867_DATA(tracking_key), RFC1867_DATA(key_length)+1, track, APCG(rfc1867_ttl), 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + RFC1867_DATA(prev_bytes_processed) = RFC1867_DATA(bytes_processed); + } + } + break; + + case MULTIPART_EVENT_FILE_END: + if(*RFC1867_DATA(tracking_key)) { + multipart_event_file_end *data = (multipart_event_file_end *) event_data; + RFC1867_DATA(bytes_processed) = data->post_bytes_processed; + RFC1867_DATA(cancel_upload) = data->cancel_upload; + RFC1867_DATA(temp_filename) = data->temp_filename; + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", RFC1867_DATA(content_length)); + add_assoc_long(track, "current", RFC1867_DATA(bytes_processed)); + add_assoc_string(track, "filename", RFC1867_DATA(filename), 1); + add_assoc_string(track, "name", RFC1867_DATA(name), 1); + add_assoc_string(track, "temp_filename", RFC1867_DATA(temp_filename), 1); + add_assoc_long(track, "cancel_upload", RFC1867_DATA(cancel_upload)); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", RFC1867_DATA(start_time)); + _apc_store(RFC1867_DATA(tracking_key), RFC1867_DATA(key_length)+1, track, APCG(rfc1867_ttl), 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + break; + + case MULTIPART_EVENT_END: + if(*RFC1867_DATA(tracking_key)) { + double now = my_time(); + multipart_event_end *data = (multipart_event_end *) event_data; + RFC1867_DATA(bytes_processed) = data->post_bytes_processed; + if(now>RFC1867_DATA(start_time)) RFC1867_DATA(rate) = 8.0*RFC1867_DATA(bytes_processed)/(now-RFC1867_DATA(start_time)); + else RFC1867_DATA(rate) = 8.0*RFC1867_DATA(bytes_processed); /* Too quick */ + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", RFC1867_DATA(content_length)); + add_assoc_long(track, "current", RFC1867_DATA(bytes_processed)); + add_assoc_double(track, "rate", RFC1867_DATA(rate)); + add_assoc_string(track, "filename", RFC1867_DATA(filename), 1); + add_assoc_string(track, "name", RFC1867_DATA(name), 1); + add_assoc_long(track, "cancel_upload", RFC1867_DATA(cancel_upload)); + add_assoc_long(track, "done", 1); + add_assoc_double(track, "start_time", RFC1867_DATA(start_time)); + _apc_store(RFC1867_DATA(tracking_key), RFC1867_DATA(key_length)+1, track, APCG(rfc1867_ttl), 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + break; + } + + return SUCCESS; +} + +#endif +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_sem.c @@ -0,0 +1,192 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_sem.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc.h" + +#ifdef APC_SEM_LOCKS + +#include "apc_sem.h" +#include "php.h" +#include +#include +#include +#include +#include + +#if HAVE_SEMUN +/* we have semun, no need to define */ +#else +#undef HAVE_SEMUN +union semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ + /* Linux specific part: */ + struct seminfo *__buf; /* buffer for IPC_INFO */ +}; +#define HAVE_SEMUN 1 +#endif + +#ifndef SEM_R +# define SEM_R 0444 +#endif +#ifndef SEM_A +# define SEM_A 0222 +#endif + +/* always use SEM_UNDO, otherwise we risk deadlock */ +#define USE_SEM_UNDO + +#ifdef USE_SEM_UNDO +# define UNDO SEM_UNDO +#else +# define UNDO 0 +#endif + +int apc_sem_create(int proj, int initval TSRMLS_DC) +{ + int semid; + int perms = 0777; + union semun arg; + key_t key = IPC_PRIVATE; + + if ((semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms)) >= 0) { + /* sempahore created for the first time, initialize now */ + arg.val = initval; + if (semctl(semid, 0, SETVAL, arg) < 0) { + apc_error("apc_sem_create: semctl(%d,...) failed:" TSRMLS_CC, semid); + } + } + else if (errno == EEXIST) { + /* sempahore already exists, don't initialize */ + if ((semid = semget(key, 1, perms)) < 0) { + apc_error("apc_sem_create: semget(%u,...) failed:" TSRMLS_CC, key); + } + /* insert here */ + } + else { + apc_error("apc_sem_create: semget(%u,...) failed:" TSRMLS_CC, key); + } + + return semid; +} + +void apc_sem_destroy(int semid) +{ + /* we expect this call to fail often, so we do not check */ + union semun arg; + semctl(semid, 0, IPC_RMID, arg); +} + +void apc_sem_lock(int semid TSRMLS_DC) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_error("apc_sem_lock: semop(%d) failed:" TSRMLS_CC, semid); + } + } +} + +int apc_sem_nonblocking_lock(int semid TSRMLS_DC) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = UNDO | IPC_NOWAIT; + + if (semop(semid, &op, 1) < 0) { + if (errno == EAGAIN) { + return 0; /* Lock is already held */ + } else if (errno != EINTR) { + apc_error("apc_sem_lock: semop(%d) failed:" TSRMLS_CC, semid); + } + } + + return 1; /* Lock obtained */ +} + +void apc_sem_unlock(int semid TSRMLS_DC) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 1; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_error("apc_sem_unlock: semop(%d) failed:" TSRMLS_CC, semid); + } + } +} + +void apc_sem_wait_for_zero(int semid TSRMLS_DC) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 0; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_error("apc_sem_waitforzero: semop(%d) failed:" TSRMLS_CC, semid); + } + } +} + +int apc_sem_get_value(int semid TSRMLS_DC) +{ + union semun arg; + unsigned short val[1]; + + arg.array = val; + if (semctl(semid, 0, GETALL, arg) < 0) { + apc_error("apc_sem_getvalue: semctl(%d,...) failed:" TSRMLS_CC, semid); + } + return val[0]; +} + +#endif /* APC_SEM_LOCKS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_sem.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_sem.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_SEM_H +#define APC_SEM_H + +/* Wrapper functions for SysV sempahores */ + +extern int apc_sem_create(int proj, int initval TSRMLS_DC); +extern void apc_sem_destroy(int semid); +extern void apc_sem_lock(int semid TSRMLS_DC); +extern int apc_sem_nonblocking_lock(int semid TSRMLS_DC); +extern void apc_sem_unlock(int semid TSRMLS_DC); +extern void apc_sem_wait_for_zero(int semid TSRMLS_DC); +extern int apc_sem_get_value(int semid TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_shm.c @@ -0,0 +1,114 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_shm.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc_shm.h" +#include "apc.h" +#ifdef PHP_WIN32 +/* shm functions are available in TSRM */ +#include +#define key_t long +#else +#include +#include +#include +#endif + +#ifndef SHM_R +# define SHM_R 0444 /* read permission */ +#endif +#ifndef SHM_A +# define SHM_A 0222 /* write permission */ +#endif + +int apc_shm_create(int proj, size_t size TSRMLS_DC) +{ + int shmid; /* shared memory id */ + int oflag; /* permissions on shm */ + key_t key = IPC_PRIVATE; /* shm key */ + + oflag = IPC_CREAT | SHM_R | SHM_A; + if ((shmid = shmget(key, size, oflag)) < 0) { + apc_error("apc_shm_create: shmget(%d, %d, %d) failed: %s. It is possible that the chosen SHM segment size is higher than the operation system allows. Linux has usually a default limit of 32MB per segment." TSRMLS_CC, key, size, oflag, strerror(errno)); + } + + return shmid; +} + +void apc_shm_destroy(int shmid) +{ + /* we expect this call to fail often, so we do not check */ + shmctl(shmid, IPC_RMID, 0); +} + +apc_segment_t apc_shm_attach(int shmid TSRMLS_DC) +{ + apc_segment_t segment; /* shm segment */ + + if ((long)(segment.shmaddr = shmat(shmid, 0, 0)) == -1) { + apc_error("apc_shm_attach: shmat failed:" TSRMLS_CC); + } + +#ifdef APC_MEMPROTECT + + if ((long)(segment.roaddr = shmat(shmid, 0, SHM_RDONLY)) == -1) { + segment.roaddr = NULL; + } + +#endif + + /* + * We set the shmid for removal immediately after attaching to it. The + * segment won't disappear until all processes have detached from it. + */ + apc_shm_destroy(shmid); + return segment; +} + +void apc_shm_detach(apc_segment_t* segment TSRMLS_DC) +{ + if (shmdt(segment->shmaddr) < 0) { + apc_error("apc_shm_detach: shmdt failed:" TSRMLS_CC); + } + +#ifdef APC_MEMPROTECT + if (segment->roaddr && shmdt(segment->roaddr) < 0) { + apc_error("apc_shm_detach: shmdt failed:" TSRMLS_CC); + } +#endif +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_shm.h @@ -0,0 +1,56 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_shm.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_SHM_H +#define APC_SHM_H + +#include +#ifdef PHP_WIN32 +#include +#endif + +#include "apc_sma.h" + +/* Wrapper functions for unix shared memory */ + +extern int apc_shm_create(int proj, size_t size TSRMLS_DC); +extern void apc_shm_destroy(int shmid); +extern apc_segment_t apc_shm_attach(int shmid TSRMLS_DC); +extern void apc_shm_detach(apc_segment_t* segment TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_signal.c @@ -0,0 +1,197 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Lucas Nealan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Facebook Inc. in 2007. + + Future revisions and derivatives of this source code must acknowledge + Facebook 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: apc_signal.c 302175 2010-08-13 06:20:28Z kalle $ */ + + /* Allows apc to install signal handlers and maintain signalling + to already registered handlers. Registers all signals that + coredump by default and unmaps the shared memory segment + before the coredump. Note: PHP module init is called before + signals are set by Apache and thus apc_set_signals should + be called in request init (RINIT) + */ + +#include "apc.h" + +#if HAVE_SIGACTION +#include +#include "apc_globals.h" +#include "apc_sma.h" +#include "apc_signal.h" + +static apc_signal_info_t apc_signal_info = {0}; + +static int apc_register_signal(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC); +static void apc_rehandle_signal(int signo, siginfo_t *siginfo, void *context); +static void apc_core_unmap(int signo, siginfo_t *siginfo, void *context); + +/* {{{ apc_core_unmap + * Coredump signal handler, unmaps shm and calls previously installed handlers + */ +static void apc_core_unmap(int signo, siginfo_t *siginfo, void *context) +{ + TSRMLS_FETCH(); + + apc_sma_cleanup(TSRMLS_C); + apc_rehandle_signal(signo, siginfo, context); + +#if !defined(WIN32) && !defined(NETWARE) + kill(getpid(), signo); +#else + raise(signo); +#endif +} /* }}} */ + +/* {{{ apc_rehandle_signal + * Call the previously registered handler for a signal + */ +static void apc_rehandle_signal(int signo, siginfo_t *siginfo, void *context) +{ + int i; + apc_signal_entry_t p_sig = {0}; + + for (i=0; (i < apc_signal_info.installed && p_sig.signo != signo); i++) { + p_sig = *apc_signal_info.prev[i]; + if (p_sig.signo == signo) { + if (p_sig.siginfo) { + (*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context); + } else { + (*(void (*)(int))p_sig.handler)(signo); + } + } + } + +} /* }}} */ + +/* {{{ apc_register_signal + * Set a handler for a previously installed signal and save so we can + * callback when handled + */ +static int apc_register_signal(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC) +{ + struct sigaction sa = {{0}}; + apc_signal_entry_t p_sig = {0}; + + if (sigaction(signo, NULL, &sa) == 0) { + if ((void*)sa.sa_handler == (void*)handler) { + return SUCCESS; + } + + if (sa.sa_handler != SIG_ERR && sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN) { + p_sig.signo = signo; + p_sig.siginfo = ((sa.sa_flags & SA_SIGINFO) == SA_SIGINFO); + p_sig.handler = (void *)sa.sa_handler; + + apc_signal_info.prev = (apc_signal_entry_t **)apc_erealloc(apc_signal_info.prev, (apc_signal_info.installed+1)*sizeof(apc_signal_entry_t *) TSRMLS_CC); + apc_signal_info.prev[apc_signal_info.installed] = (apc_signal_entry_t *)apc_emalloc(sizeof(apc_signal_entry_t) TSRMLS_CC); + *apc_signal_info.prev[apc_signal_info.installed++] = p_sig; + } else { + /* inherit flags and mask if already set */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_flags |= SA_SIGINFO; /* we'll use a siginfo handler */ +#if defined(SA_ONESHOT) + sa.sa_flags = SA_ONESHOT; +#elif defined(SA_RESETHAND) + sa.sa_flags = SA_RESETHAND; +#endif + } + sa.sa_handler = (void*)handler; + + if (sigaction(signo, &sa, NULL) < 0) { + apc_warning("Error installing apc signal handler for %d" TSRMLS_CC, signo); + } + + return SUCCESS; + } + return FAILURE; +} /* }}} */ + +/* {{{ apc_set_signals + * Install our signal handlers */ +void apc_set_signals(TSRMLS_D) +{ + if (APCG(coredump_unmap) && apc_signal_info.installed == 0) { + /* ISO C standard signals that coredump */ + apc_register_signal(SIGSEGV, apc_core_unmap TSRMLS_CC); + apc_register_signal(SIGABRT, apc_core_unmap TSRMLS_CC); + apc_register_signal(SIGFPE, apc_core_unmap TSRMLS_CC); + apc_register_signal(SIGILL, apc_core_unmap TSRMLS_CC); + /* extended signals that coredump */ +#ifdef SIGBUS + apc_register_signal(SIGBUS, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGABORT + apc_register_signal(SIGABORT, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGEMT + apc_register_signal(SIGEMT, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGIOT + apc_register_signal(SIGIOT, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGQUIT + apc_register_signal(SIGQUIT, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGSYS + apc_register_signal(SIGSYS, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGTRAP + apc_register_signal(SIGTRAP, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGXCPU + apc_register_signal(SIGXCPU, apc_core_unmap TSRMLS_CC); +#endif +#ifdef SIGXFSZ + apc_register_signal(SIGXFSZ, apc_core_unmap TSRMLS_CC); +#endif + } +} /* }}} */ + +/* {{{ apc_set_signals + * cleanup signals for shutdown */ +void apc_shutdown_signals(TSRMLS_D) +{ + int i=0; + if (apc_signal_info.installed > 0) { + for (i=0; (i < apc_signal_info.installed); i++) { + apc_efree(apc_signal_info.prev[i] TSRMLS_CC); + } + apc_efree(apc_signal_info.prev TSRMLS_CC); + apc_signal_info.installed = 0; /* just in case */ + } +} + +#endif /* HAVE_SIGACTION */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_signal.h @@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Lucas Nealan | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_signal.h 300986 2010-07-04 14:41:33Z felipe $ */ + +#ifndef APC_SIGNAL_H +#define APC_SIGNAL_H + +#include "apc.h" +#include "apc_php.h" + +typedef struct apc_signal_entry_t { + int signo; /* signal number */ + int siginfo; /* siginfo style handler calling */ + void* handler; /* signal handler */ +} apc_signal_entry_t; + +typedef struct apc_signal_info_t { + int installed; /* How many signals we've installed handles for */ + apc_signal_entry_t **prev; /* Previous signal handlers */ +} apc_signal_info_t; + +void apc_set_signals(TSRMLS_D); +void apc_shutdown_signals(TSRMLS_D); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_sma.c @@ -0,0 +1,765 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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: apc_sma.c 305260 2010-11-10 19:23:02Z gopalv $ */ + +#include "apc_sma.h" +#include "apc.h" +#include "apc_globals.h" +#include "apc_lock.h" +#include "apc_shm.h" +#include "apc_cache.h" + +#include +#include "apc_mmap.h" + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +enum { DEFAULT_NUMSEG=1, DEFAULT_SEGSIZE=30*1024*1024 }; + +static int sma_initialized = 0; /* true if the sma has been initialized */ +static uint sma_numseg; /* number of shm segments to allow */ +static size_t sma_segsize; /* size of each shm segment */ +static apc_segment_t* sma_segments; /* array of shm segments */ +static int sma_lastseg = 0; /* index of MRU segment */ + +typedef struct sma_header_t sma_header_t; +struct sma_header_t { + apc_lck_t sma_lock; /* segment lock, MUST BE ALIGNED for futex locks */ + size_t segsize; /* size of entire segment */ + size_t avail; /* bytes available (not necessarily contiguous) */ +#if ALLOC_DISTRIBUTION + size_t adist[30]; +#endif +}; + +#define SMA_HDR(i) ((sma_header_t*)((sma_segments[i]).shmaddr)) +#define SMA_ADDR(i) ((char*)(SMA_HDR(i))) +#define SMA_RO(i) ((char*)(sma_segments[i]).roaddr) +#define SMA_LCK(i) ((SMA_HDR(i))->sma_lock) + + +/* do not enable for threaded http servers */ +/* #define __APC_SMA_DEBUG__ 1 */ + +#ifdef __APC_SMA_DEBUG__ +/* global counter for identifying blocks + * Technically it is possible to do the same + * using offsets, but double allocations of the + * same offset can happen. */ +static volatile size_t block_id = 0; +#endif + +#define APC_SMA_CANARIES 1 + +typedef struct block_t block_t; +struct block_t { + size_t size; /* size of this block */ + size_t prev_size; /* size of sequentially previous block, 0 if prev is allocated */ + size_t fnext; /* offset in segment of next free block */ + size_t fprev; /* offset in segment of prev free block */ +#ifdef APC_SMA_CANARIES + size_t canary; /* canary to check for memory overwrites */ +#endif +#ifdef __APC_SMA_DEBUG__ + size_t id; /* identifier for the memory block */ +#endif +}; + +/* The macros BLOCKAT and OFFSET are used for convenience throughout this + * module. Both assume the presence of a variable shmaddr that points to the + * beginning of the shared memory segment in question. */ + +#define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset)) +#define OFFSET(block) ((size_t)(((char*)block) - (char*)shmaddr)) + +/* macros for getting the next or previous sequential block */ +#define NEXT_SBLOCK(block) ((block_t*)((char*)block + block->size)) +#define PREV_SBLOCK(block) (block->prev_size ? ((block_t*)((char*)block - block->prev_size)) : NULL) + +/* Canary macros for setting, checking and resetting memory canaries */ +#ifdef APC_SMA_CANARIES + #define SET_CANARY(v) (v)->canary = 0x42424242 + #define CHECK_CANARY(v) assert((v)->canary == 0x42424242) + #define RESET_CANARY(v) (v)->canary = -42 +#else + #define SET_CANARY(v) + #define CHECK_CANARY(v) + #define RESET_CANARY(v) +#endif + + +/* {{{ MINBLOCKSIZE */ +#define MINBLOCKSIZE (ALIGNWORD(1) + ALIGNWORD(sizeof(block_t))) +/* }}} */ + +#if 0 +/* {{{ sma_debug_state(apc_sma_segment_t *segment, int canary_check, int verbose) + * useful for debuging state of memory blocks and free list, and sanity checking + */ +static void sma_debug_state(void* shmaddr, int canary_check, int verbose TSRMLS_DC) { + sma_header_t *header = (sma_header_t*)shmaddr; + block_t *cur = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + block_t *prv = NULL; + size_t avail; + + /* Verify free list */ + if (verbose) apc_warning("Free List: " TSRMLS_CC); + while(1) { + if (verbose) apc_warning(" 0x%x[%d] (s%d)" TSRMLS_CC, cur, OFFSET(cur), cur->size); + if (canary_check) CHECK_CANARY(cur); + if (!cur->fnext) break; + cur = BLOCKAT(cur->fnext); + avail += cur->size; + if (prv == cur) { + apc_warning("Circular list detected!" TSRMLS_CC); + assert(0); + } + if (prv && cur->fprev != OFFSET(prv)) { + apc_warning("Previous pointer does not point to previous!" TSRMLS_CC); + assert(0); + } + prv = cur; + } + assert(avail == header->avail); + + /* Verify each block */ + if (verbose) apc_warning("Block List: " TSRMLS_CC); + cur = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + while(1) { + if(!cur->fnext) { + if (verbose) apc_warning(" 0x%x[%d] (s%d) (u)" TSRMLS_CC, cur, OFFSET(cur), cur->size); + } else { + if (verbose) apc_warning(" 0x%x[%d] (s%d) (f)" TSRMLS_CC, cur, OFFSET(cur), cur->size); + } + if (canary_check) CHECK_CANARY(cur); + if (!cur->size && !cur->fnext) break; + if (!cur->size) { + cur = BLOCKAT(OFFSET(cur) + ALIGNWORD(sizeof(block_t))); + } else { + cur = NEXT_SBLOCK(cur); + } + if (prv == cur) { + apc_warning("Circular list detected!" TSRMLS_CC); + assert(0); + } + prv = cur; + } +} +/* }}} */ +#endif + +/* {{{ sma_allocate: tries to allocate at least size bytes in a segment */ +static size_t sma_allocate(sma_header_t* header, size_t size, size_t fragment, size_t *allocated) +{ + void* shmaddr; /* header of shared memory segment */ + block_t* prv; /* block prior to working block */ + block_t* cur; /* working block in list */ + block_t* prvnextfit; /* block before next fit */ + size_t realsize; /* actual size of block needed, including header */ + const size_t block_size = ALIGNWORD(sizeof(struct block_t)); + + realsize = ALIGNWORD(size + block_size); + + /* + * First, insure that the segment contains at least realsize free bytes, + * even if they are not contiguous. + */ + shmaddr = header; + + if (header->avail < realsize) { + return -1; + } + + prvnextfit = 0; /* initially null (no fit) */ + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + CHECK_CANARY(prv); + + while (prv->fnext != 0) { + cur = BLOCKAT(prv->fnext); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(cur); +#endif + /* If it can fit realsize bytes in cur block, stop searching */ + if (cur->size >= realsize) { + prvnextfit = prv; + break; + } + prv = cur; + } + + if (prvnextfit == 0) { + return -1; + } + + prv = prvnextfit; + cur = BLOCKAT(prv->fnext); + + CHECK_CANARY(prv); + CHECK_CANARY(cur); + + if (cur->size == realsize || (cur->size > realsize && cur->size < (realsize + (MINBLOCKSIZE + fragment)))) { + /* cur is big enough for realsize, but too small to split - unlink it */ + *(allocated) = cur->size - block_size; + prv->fnext = cur->fnext; + BLOCKAT(cur->fnext)->fprev = OFFSET(prv); + NEXT_SBLOCK(cur)->prev_size = 0; /* block is alloc'd */ + } else { + /* nextfit is too big; split it into two smaller blocks */ + block_t* nxt; /* the new block (chopped part of cur) */ + size_t oldsize; /* size of cur before split */ + + oldsize = cur->size; + cur->size = realsize; + *(allocated) = cur->size - block_size; + nxt = NEXT_SBLOCK(cur); + nxt->prev_size = 0; /* block is alloc'd */ + nxt->size = oldsize - realsize; /* and fix the size */ + NEXT_SBLOCK(nxt)->prev_size = nxt->size; /* adjust size */ + SET_CANARY(nxt); + + /* replace cur with next in free list */ + nxt->fnext = cur->fnext; + nxt->fprev = cur->fprev; + BLOCKAT(nxt->fnext)->fprev = OFFSET(nxt); + BLOCKAT(nxt->fprev)->fnext = OFFSET(nxt); +#ifdef __APC_SMA_DEBUG__ + nxt->id = -1; +#endif + } + + cur->fnext = 0; + + /* update the block header */ + header->avail -= cur->size; +#if ALLOC_DISTRIBUTION + header->adist[(int)(log(size)/log(2))]++; +#endif + + SET_CANARY(cur); +#ifdef __APC_SMA_DEBUG__ + cur->id = ++block_id; + fprintf(stderr, "allocate(realsize=%d,size=%d,id=%d)\n", (int)(size), (int)(cur->size), cur->id); +#endif + + return OFFSET(cur) + block_size; +} +/* }}} */ + +/* {{{ sma_deallocate: deallocates the block at the given offset */ +static size_t sma_deallocate(void* shmaddr, size_t offset) +{ + sma_header_t* header; /* header of shared memory segment */ + block_t* cur; /* the new block to insert */ + block_t* prv; /* the block before cur */ + block_t* nxt; /* the block after cur */ + size_t size; /* size of deallocated block */ + + offset -= ALIGNWORD(sizeof(struct block_t)); + assert(offset >= 0); + + /* find position of new block in free list */ + cur = BLOCKAT(offset); + + /* update the block header */ + header = (sma_header_t*) shmaddr; + header->avail += cur->size; + size = cur->size; + + if (cur->prev_size != 0) { + /* remove prv from list */ + prv = PREV_SBLOCK(cur); + BLOCKAT(prv->fnext)->fprev = prv->fprev; + BLOCKAT(prv->fprev)->fnext = prv->fnext; + /* cur and prv share an edge, combine them */ + prv->size +=cur->size; + RESET_CANARY(cur); + cur = prv; + } + + nxt = NEXT_SBLOCK(cur); + if (nxt->fnext != 0) { + assert(NEXT_SBLOCK(NEXT_SBLOCK(cur))->prev_size == nxt->size); + /* cur and nxt shared an edge, combine them */ + BLOCKAT(nxt->fnext)->fprev = nxt->fprev; + BLOCKAT(nxt->fprev)->fnext = nxt->fnext; + cur->size += nxt->size; +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(nxt); + nxt->id = -1; /* assert this or set it ? */ +#endif + RESET_CANARY(nxt); + } + + NEXT_SBLOCK(cur)->prev_size = cur->size; + + /* insert new block after prv */ + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + cur->fnext = prv->fnext; + prv->fnext = OFFSET(cur); + cur->fprev = OFFSET(prv); + BLOCKAT(cur->fnext)->fprev = OFFSET(cur); + + return size; +} +/* }}} */ + +/* {{{ apc_sma_init */ + +void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask TSRMLS_DC) +{ + uint i; + + if (sma_initialized) { + return; + } + sma_initialized = 1; + +#if APC_MMAP + /* + * I don't think multiple anonymous mmaps makes any sense + * so force sma_numseg to 1 in this case + */ + if(!mmap_file_mask || + (mmap_file_mask && !strlen(mmap_file_mask)) || + (mmap_file_mask && !strcmp(mmap_file_mask, "/dev/zero"))) { + sma_numseg = 1; + } else { + sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG; + } +#else + sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG; +#endif + + sma_segsize = segsize > 0 ? segsize : DEFAULT_SEGSIZE; + + sma_segments = (apc_segment_t*) apc_emalloc((sma_numseg * sizeof(apc_segment_t)) TSRMLS_CC); + + for (i = 0; i < sma_numseg; i++) { + sma_header_t* header; + block_t *first, *empty, *last; + void* shmaddr; + +#if APC_MMAP + sma_segments[i] = apc_mmap(mmap_file_mask, sma_segsize TSRMLS_CC); + if(sma_numseg != 1) memcpy(&mmap_file_mask[strlen(mmap_file_mask)-6], "XXXXXX", 6); +#else + sma_segments[i] = apc_shm_attach(apc_shm_create(i, sma_segsize TSRMLS_CC) TSRMLS_CC); +#endif + + sma_segments[i].size = sma_segsize; + + shmaddr = sma_segments[i].shmaddr; + + header = (sma_header_t*) shmaddr; + apc_lck_create(NULL, 0, 1, header->sma_lock); + header->segsize = sma_segsize; + header->avail = sma_segsize - ALIGNWORD(sizeof(sma_header_t)) - ALIGNWORD(sizeof(block_t)) - ALIGNWORD(sizeof(block_t)); +#if ALLOC_DISTRIBUTION + { + int j; + for(j=0; j<30; j++) header->adist[j] = 0; + } +#endif + first = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + first->size = 0; + first->fnext = ALIGNWORD(sizeof(sma_header_t)) + ALIGNWORD(sizeof(block_t)); + first->fprev = 0; + first->prev_size = 0; + SET_CANARY(first); +#ifdef __APC_SMA_DEBUG__ + block->id = -1; +#endif + empty = BLOCKAT(first->fnext); + empty->size = header->avail - ALIGNWORD(sizeof(block_t)); + empty->fnext = OFFSET(empty) + empty->size; + empty->fprev = ALIGNWORD(sizeof(sma_header_t)); + empty->prev_size = 0; + SET_CANARY(empty); +#ifdef __APC_SMA_DEBUG__ + empty->id = -1; +#endif + last = BLOCKAT(empty->fnext); + last->size = 0; + last->fnext = 0; + last->fprev = OFFSET(empty); + last->prev_size = empty->size; + SET_CANARY(last); +#ifdef __APC_SMA_DEBUG__ + last->id = -1; +#endif + } +} +/* }}} */ + +/* {{{ apc_sma_cleanup */ +void apc_sma_cleanup(TSRMLS_D) +{ + uint i; + + assert(sma_initialized); + + for (i = 0; i < sma_numseg; i++) { + apc_lck_destroy(SMA_LCK(i)); +#if APC_MMAP + apc_unmap(&sma_segments[i] TSRMLS_CC); +#else + apc_shm_detach(&sma_segments[i] TSRMLS_CC); +#endif + } + sma_initialized = 0; + apc_efree(sma_segments TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_sma_malloc_ex */ +void* apc_sma_malloc_ex(size_t n, size_t fragment, size_t* allocated TSRMLS_DC) +{ + size_t off; + uint i; + int nuked = 0; + +restart: + assert(sma_initialized); + LOCK(SMA_LCK(sma_lastseg)); + + off = sma_allocate(SMA_HDR(sma_lastseg), n, fragment, allocated); + + if(off == -1 && APCG(current_cache)) { + /* retry failed allocation after we expunge */ + UNLOCK(SMA_LCK(sma_lastseg)); + APCG(current_cache)->expunge_cb(APCG(current_cache), (n+fragment) TSRMLS_CC); + LOCK(SMA_LCK(sma_lastseg)); + off = sma_allocate(SMA_HDR(sma_lastseg), n, fragment, allocated); + } + + if (off != -1) { + void* p = (void *)(SMA_ADDR(sma_lastseg) + off); + UNLOCK(SMA_LCK(sma_lastseg)); +#ifdef VALGRIND_MALLOCLIKE_BLOCK + VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0); +#endif + return p; + } + + UNLOCK(SMA_LCK(sma_lastseg)); + + for (i = 0; i < sma_numseg; i++) { + if (i == sma_lastseg) { + continue; + } + LOCK(SMA_LCK(i)); + off = sma_allocate(SMA_HDR(i), n, fragment, allocated); + if(off == -1 && APCG(current_cache)) { + /* retry failed allocation after we expunge */ + UNLOCK(SMA_LCK(i)); + APCG(current_cache)->expunge_cb(APCG(current_cache), (n+fragment) TSRMLS_CC); + LOCK(SMA_LCK(i)); + off = sma_allocate(SMA_HDR(i), n, fragment, allocated); + } + if (off != -1) { + void* p = (void *)(SMA_ADDR(i) + off); + UNLOCK(SMA_LCK(i)); + sma_lastseg = i; +#ifdef VALGRIND_MALLOCLIKE_BLOCK + VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0); +#endif + return p; + } + UNLOCK(SMA_LCK(i)); + } + + /* I've tried being nice, but now you're just asking for it */ + if(!nuked) { + apc_cache->expunge_cb(apc_cache, (n+fragment) TSRMLS_CC); + apc_user_cache->expunge_cb(apc_cache, (n+fragment) TSRMLS_CC); + nuked = 1; + goto restart; + } + + /* now, I've truly and well given up */ + + return NULL; +} +/* }}} */ + +/* {{{ apc_sma_malloc */ +void* apc_sma_malloc(size_t n TSRMLS_DC) +{ + size_t allocated; + void *p = apc_sma_malloc_ex(n, MINBLOCKSIZE, &allocated TSRMLS_CC); + + return p; +} +/* }}} */ + +/* {{{ apc_sma_realloc */ +void* apc_sma_realloc(void *p, size_t n TSRMLS_DC) +{ + apc_sma_free(p TSRMLS_CC); + return apc_sma_malloc(n TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_sma_strdup */ +char* apc_sma_strdup(const char* s TSRMLS_DC) +{ + void* q; + int len; + + if(!s) return NULL; + + len = strlen(s)+1; + q = apc_sma_malloc(len TSRMLS_CC); + if(!q) return NULL; + memcpy(q, s, len); + return q; +} +/* }}} */ + +/* {{{ apc_sma_free */ +void apc_sma_free(void* p TSRMLS_DC) +{ + uint i; + size_t offset; + size_t d_size; + + if (p == NULL) { + return; + } + + assert(sma_initialized); + + + for (i = 0; i < sma_numseg; i++) { + offset = (size_t)((char *)p - SMA_ADDR(i)); + if (p >= (void*)SMA_ADDR(i) && offset < sma_segsize) { + LOCK(SMA_LCK(i)); + d_size = sma_deallocate(SMA_HDR(i), offset); + UNLOCK(SMA_LCK(i)); +#ifdef VALGRIND_FREELIKE_BLOCK + VALGRIND_FREELIKE_BLOCK(p, 0); +#endif + return; + } + } + + apc_error("apc_sma_free: could not locate address %p" TSRMLS_CC, p); +} +/* }}} */ + +#ifdef APC_MEMPROTECT +/* {{{ */ +void* apc_sma_protect(void *p) +{ + unsigned int i = 0; + size_t offset; + + if (p == NULL) { + return NULL; + } + + if(SMA_RO(sma_lastseg) == NULL) return p; + + offset = (size_t)((char *)p - SMA_ADDR(sma_lastseg)); + + if(p >= (void*)SMA_ADDR(sma_lastseg) && offset < sma_segsize) { + return SMA_RO(sma_lastseg) + offset; + } + + for (i = 0; i < sma_numseg; i++) { + offset = (size_t)((char *)p - SMA_ADDR(i)); + if (p >= (void*)SMA_ADDR(i) && offset < sma_segsize) { + return SMA_RO(i) + offset; + } + } + + return NULL; +} +/* }}} */ + +/* {{{ */ +void* apc_sma_unprotect(void *p) +{ + unsigned int i = 0; + size_t offset; + + if (p == NULL) { + return NULL; + } + + if(SMA_RO(sma_lastseg) == NULL) return p; + + offset = (size_t)((char *)p - SMA_RO(sma_lastseg)); + + if(p >= (void*)SMA_RO(sma_lastseg) && offset < sma_segsize) { + return SMA_ADDR(sma_lastseg) + offset; + } + + for (i = 0; i < sma_numseg; i++) { + offset = (size_t)((char *)p - SMA_RO(i)); + if (p >= (void*)SMA_RO(i) && offset < sma_segsize) { + return SMA_ADDR(i) + offset; + } + } + + return NULL; +} +/* }}} */ +#else +/* {{{ */ +void* apc_sma_protect(void *p) { return p; } +void* apc_sma_unprotect(void *p) { return p; } +/* }}} */ +#endif + +/* {{{ apc_sma_info */ +apc_sma_info_t* apc_sma_info(zend_bool limited TSRMLS_DC) +{ + apc_sma_info_t* info; + apc_sma_link_t** link; + uint i; + char* shmaddr; + block_t* prv; + + if (!sma_initialized) { + return NULL; + } + + info = (apc_sma_info_t*) apc_emalloc(sizeof(apc_sma_info_t) TSRMLS_CC); + info->num_seg = sma_numseg; + info->seg_size = sma_segsize - (ALIGNWORD(sizeof(sma_header_t)) + ALIGNWORD(sizeof(block_t)) + ALIGNWORD(sizeof(block_t))); + + info->list = apc_emalloc(info->num_seg * sizeof(apc_sma_link_t*) TSRMLS_CC); + for (i = 0; i < sma_numseg; i++) { + info->list[i] = NULL; + } + + if(limited) return info; + + /* For each segment */ + for (i = 0; i < sma_numseg; i++) { + RDLOCK(SMA_LCK(i)); + shmaddr = SMA_ADDR(i); + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); + + link = &info->list[i]; + + /* For each block in this segment */ + while (BLOCKAT(prv->fnext)->fnext != 0) { + block_t* cur = BLOCKAT(prv->fnext); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(cur); +#endif + + *link = apc_emalloc(sizeof(apc_sma_link_t) TSRMLS_CC); + (*link)->size = cur->size; + (*link)->offset = prv->fnext; + (*link)->next = NULL; + link = &(*link)->next; + + prv = cur; + +#if ALLOC_DISTRIBUTION + sma_header_t* header = (sma_header_t*) segment->shmaddr; + memcpy(info->seginfo[i].adist, header->adist, sizeof(size_t) * 30); +#endif + + } + UNLOCK(SMA_LCK(i)); + } + + return info; +} +/* }}} */ + +/* {{{ apc_sma_free_info */ +void apc_sma_free_info(apc_sma_info_t* info TSRMLS_DC) +{ + int i; + + for (i = 0; i < info->num_seg; i++) { + apc_sma_link_t* p = info->list[i]; + while (p) { + apc_sma_link_t* q = p; + p = p->next; + apc_efree(q TSRMLS_CC); + } + } + apc_efree(info->list TSRMLS_CC); + apc_efree(info TSRMLS_CC); +} +/* }}} */ + +/* {{{ apc_sma_get_avail_mem */ +size_t apc_sma_get_avail_mem() +{ + size_t avail_mem = 0; + uint i; + + for (i = 0; i < sma_numseg; i++) { + sma_header_t* header = SMA_HDR(i); + avail_mem += header->avail; + } + return avail_mem; +} +/* }}} */ + +/* {{{ apc_sma_get_avail_size */ +zend_bool apc_sma_get_avail_size(size_t size) +{ + uint i; + + for (i = 0; i < sma_numseg; i++) { + sma_header_t* header = SMA_HDR(i); + if (header->avail > size) { + return 1; + } + } + return 0; +} +/* }}} */ + + +#if ALLOC_DISTRIBUTION +size_t *apc_sma_get_alloc_distribution(void) { + sma_header_t* header = (sma_header_t*) segment->sma_shmaddr; + return header->adist; +} +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_sma.h @@ -0,0 +1,103 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_sma.h 303274 2010-09-11 14:31:32Z iliaa $ */ + +#ifndef APC_SMA_H +#define APC_SMA_H + +#define ALLOC_DISTRIBUTION 0 + +#include "apc.h" + +/* Simple shared memory allocator */ + +typedef struct _apc_segment_t apc_segment_t; + +struct _apc_segment_t { + size_t size; + void* shmaddr; +#ifdef APC_MEMPROTECT + void* roaddr; +#endif +}; + +extern void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask TSRMLS_DC); +extern void apc_sma_cleanup(TSRMLS_D); +extern void* apc_sma_malloc(size_t size TSRMLS_DC); +extern void* apc_sma_malloc_ex(size_t size, size_t fragment, size_t* allocated TSRMLS_DC); +extern void* apc_sma_realloc(void* p, size_t size TSRMLS_DC); +extern char* apc_sma_strdup(const char *s TSRMLS_DC); +extern void apc_sma_free(void* p TSRMLS_DC); +#if ALLOC_DISTRIBUTION +extern size_t *apc_sma_get_alloc_distribution(); +#endif + +extern void* apc_sma_protect(void *p); +extern void* apc_sma_unprotect(void *p); + +/* {{{ struct definition: apc_sma_link_t */ +typedef struct apc_sma_link_t apc_sma_link_t; +struct apc_sma_link_t { + long size; /* size of this free block */ + long offset; /* offset in segment of this block */ + apc_sma_link_t* next; /* link to next free block */ +}; +/* }}} */ + +/* {{{ struct definition: apc_sma_info_t */ +typedef struct apc_sma_info_t apc_sma_info_t; +struct apc_sma_info_t { + int num_seg; /* number of shared memory segments */ + size_t seg_size; /* size of each shared memory segment */ + apc_sma_link_t** list; /* there is one list per segment */ +}; +/* }}} */ + +extern apc_sma_info_t* apc_sma_info(zend_bool limited TSRMLS_DC); +extern void apc_sma_free_info(apc_sma_info_t* info TSRMLS_DC); + +extern size_t apc_sma_get_avail_mem(); +extern zend_bool apc_sma_get_avail_size(size_t size); +extern void apc_sma_check_integrity(); + +/* {{{ ALIGNWORD: pad up x, aligned to the system's word boundary */ +typedef union { void* p; int i; long l; double d; void (*f)(); } apc_word_t; +#define ALIGNSIZE(x, size) ((size) * (1 + (((x)-1)/(size)))) +#define ALIGNWORD(x) ALIGNSIZE(x, sizeof(apc_word_t)) +/* }}} */ + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_spin.c @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_spin.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc_spin.h" + +#ifdef APC_SPIN_LOCKS + +slock_t *apc_slock_create(slock_t *lock) +{ + S_INIT_LOCK(lock); + return lock; +} + +void apc_slock_destroy(slock_t *lock) +{ + return; +} + +void apc_slock_lock(slock_t *lock TSRMLS_DC) +{ + S_LOCK(lock); +} + +void apc_slock_unlock(slock_t *lock) +{ + S_UNLOCK(lock); +} + +zend_bool apc_slock_nonblocking_lock(slock_t *lock) +{ + /* Technically we aren't supposed to call this directly, but the original + * code provides no method for absolute non-blocking locks, so we'll call into + * the TAS (test and set) functionality directly + */ + return !(TAS(lock)); /* if TAS returns 0 we obtained the lock, otherwise we failed */ +} + + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_spin.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_spin.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_SPIN_H +#define APC_SPIN_H + +#include "apc.h" + +#ifdef APC_SPIN_LOCKS + +#include "pgsql_s_lock.h" + +slock_t *apc_slock_create(slock_t *lock); +void apc_slock_destroy(slock_t *lock); +void apc_slock_lock(slock_t *lock TSRMLS_DC); +zend_bool apc_slock_nonblocking_lock(slock_t *lock); +void apc_slock_unlock(slock_t *lock); + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_stack.c @@ -0,0 +1,106 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_stack.c 302175 2010-08-13 06:20:28Z kalle $ */ + +#include "apc.h" +#include "apc_stack.h" + +struct apc_stack_t { + void** data; + int capacity; + int size; +}; + +apc_stack_t* apc_stack_create(int size_hint TSRMLS_DC) +{ + apc_stack_t* stack = (apc_stack_t*) apc_emalloc(sizeof(apc_stack_t) TSRMLS_CC); + + stack->capacity = (size_hint > 0) ? size_hint : 10; + stack->size = 0; + stack->data = (void**) apc_emalloc(sizeof(void*) * stack->capacity TSRMLS_CC); + + return stack; +} + +void apc_stack_destroy(apc_stack_t* stack TSRMLS_DC) +{ + if (stack != NULL) { + apc_efree(stack->data TSRMLS_CC); + apc_efree(stack TSRMLS_CC); + } +} + +void apc_stack_clear(apc_stack_t* stack) +{ + assert(stack != NULL); + stack->size = 0; +} + +void apc_stack_push(apc_stack_t* stack, void* item TSRMLS_DC) +{ + assert(stack != NULL); + if (stack->size == stack->capacity) { + stack->capacity *= 2; + stack->data = apc_erealloc(stack->data, sizeof(void*)*stack->capacity TSRMLS_CC); + } + stack->data[stack->size++] = item; +} + +void* apc_stack_pop(apc_stack_t* stack) +{ + assert(stack != NULL && stack->size > 0); + return stack->data[--stack->size]; +} + +void* apc_stack_top(apc_stack_t* stack) +{ + assert(stack != NULL && stack->size > 0); + return stack->data[stack->size-1]; +} + +void* apc_stack_get(apc_stack_t* stack, int n) +{ + assert(stack != NULL && stack->size > n); + return stack->data[n]; +} + +int apc_stack_size(apc_stack_t* stack) +{ + assert(stack != NULL); + return stack->size; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_stack.h @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + +----------------------------------------------------------------------+ + + 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: apc_stack.h 302175 2010-08-13 06:20:28Z kalle $ */ + +#ifndef APC_STACK_H +#define APC_STACK_H + +/* Basic stack datatype */ + +#define T apc_stack_t* +typedef struct apc_stack_t apc_stack_t; /* opaque stack type */ + +extern T apc_stack_create(int size_hint TSRMLS_DC); +extern void apc_stack_destroy(T stack TSRMLS_DC); +extern void apc_stack_clear(T stack); +extern void apc_stack_push(T stack, void* item TSRMLS_DC); +extern void* apc_stack_pop(T stack); +extern void* apc_stack_top(T stack); +extern void* apc_stack_get(T stack, int n); +extern int apc_stack_size(T stack); + +#undef T +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_string.c @@ -0,0 +1,247 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Dmitry Stogov | + +----------------------------------------------------------------------+ + + 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: $ */ + +#include "apc.h" +#include "apc_globals.h" +#include "apc_php.h" +#include "apc_lock.h" + +#ifdef ZEND_ENGINE_2_4 + +typedef struct _apc_interned_strings_data_t { + char *interned_strings_start; + char *interned_strings_end; + char *interned_strings_top; + apc_lck_t lock; + HashTable interned_strings; +} apc_interned_strings_data_t; + +apc_interned_strings_data_t *apc_interned_strings_data = NULL; + +#define APCSG(v) (apc_interned_strings_data->v) + +static char *old_interned_strings_start; +static char *old_interned_strings_end; +static char *(*old_new_interned_string)(char *str, int len, int free_src TSRMLS_DC); +static void (*old_interned_strings_snapshot)(TSRMLS_D); +static void (*old_interned_strings_restore)(TSRMLS_D); + +static char *apc_dummy_new_interned_string_for_php(char *str, int len, int free_src TSRMLS_DC) +{ + return str; +} + +static void apc_dummy_interned_strings_snapshot_for_php(TSRMLS_D) +{ +} + +static void apc_dummy_interned_strings_restore_for_php(TSRMLS_D) +{ +} + +char *apc_new_interned_string(char *arKey, int nKeyLength TSRMLS_DC) +{ + ulong h; + uint nIndex; + Bucket *p; + + if (arKey >= APCSG(interned_strings_start) && arKey < APCSG(interned_strings_end)) { + return arKey; + } + + h = zend_inline_hash_func(arKey, nKeyLength); + nIndex = h & APCSG(interned_strings).nTableMask; + + p = APCSG(interned_strings).arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + return p->arKey; + } + } + p = p->pNext; + } + + if (APCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >= + APCSG(interned_strings_end)) { + /* no memory */ + return NULL; + } + + p = (Bucket *) APCSG(interned_strings_top); + APCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength); + + p->arKey = (char*)(p+1); + memcpy(p->arKey, arKey, nKeyLength); + p->nKeyLength = nKeyLength; + p->h = h; + p->pData = &p->pDataPtr; + p->pDataPtr = p; + + p->pNext = APCSG(interned_strings).arBuckets[nIndex]; + p->pLast = NULL; + if (p->pNext) { + p->pNext->pLast = p; + } + APCSG(interned_strings).arBuckets[nIndex] = p; + + p->pListLast = APCSG(interned_strings).pListTail; + APCSG(interned_strings).pListTail = p; + p->pListNext = NULL; + if (p->pListLast != NULL) { + p->pListLast->pListNext = p; + } + if (!APCSG(interned_strings).pListHead) { + APCSG(interned_strings).pListHead = p; + } + + APCSG(interned_strings).nNumOfElements++; + + return p->arKey; +} + +static void apc_copy_internal_strings(TSRMLS_D) +{ + Bucket *p, *q; + + p = CG(function_table)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = apc_new_interned_string(p->arKey, p->nKeyLength TSRMLS_CC); + } + p = p->pListNext; + } + + p = CG(class_table)->pListHead; + while (p) { + zend_class_entry *ce = (zend_class_entry*)(p->pDataPtr); + + if (p->nKeyLength) { + p->arKey = apc_new_interned_string(p->arKey, p->nKeyLength TSRMLS_CC); + } + + q = ce->properties_info.pListHead; + while (q) { + zend_property_info *info = (zend_property_info*)(q->pData); + + if (q->nKeyLength) { + q->arKey = apc_new_interned_string(q->arKey, q->nKeyLength TSRMLS_CC); + } + + if (info->name) { + info->name = apc_new_interned_string(info->name, info->name_length+1 TSRMLS_CC); + } + + q = q->pListNext; + } + + q = ce->function_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = apc_new_interned_string(q->arKey, q->nKeyLength TSRMLS_CC); + } + q = q->pListNext; + } + + q = ce->constants_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = apc_new_interned_string(q->arKey, q->nKeyLength TSRMLS_CC); + } + q = q->pListNext; + } + + p = p->pListNext; + } + + p = EG(zend_constants)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = apc_new_interned_string(p->arKey, p->nKeyLength TSRMLS_CC); + } + p = p->pListNext; + } +} + +void apc_interned_strings_init(TSRMLS_D) +{ + int count = APCG(shm_strings_buffer) / (sizeof(Bucket) + sizeof(Bucket*) * 2); + + apc_interned_strings_data = (apc_interned_strings_data_t*) apc_sma_malloc(APCG(shm_strings_buffer) TSRMLS_CC); + + CREATE_LOCK(APCSG(lock)); + + zend_hash_init(&APCSG(interned_strings), count, NULL, NULL, 1); + APCSG(interned_strings).nTableMask = APCSG(interned_strings).nTableSize - 1; + APCSG(interned_strings).arBuckets = (Bucket**)((char*)apc_interned_strings_data + sizeof(apc_interned_strings_data_t)); + + APCSG(interned_strings_start) = (char*)APCSG(interned_strings).arBuckets + APCSG(interned_strings).nTableSize * sizeof(Bucket *); + APCSG(interned_strings_end) = (char*)apc_interned_strings_data + APCG(shm_strings_buffer); + APCSG(interned_strings_top) = APCSG(interned_strings_start); + + old_interned_strings_start = CG(interned_strings_start); + old_interned_strings_end = CG(interned_strings_end); + old_new_interned_string = zend_new_interned_string; + old_interned_strings_snapshot = zend_interned_strings_snapshot; + old_interned_strings_restore = zend_interned_strings_restore; + + CG(interned_strings_start) = APCSG(interned_strings_start); + CG(interned_strings_end) = APCSG(interned_strings_end); + zend_new_interned_string = apc_dummy_new_interned_string_for_php; + zend_interned_strings_snapshot = apc_dummy_interned_strings_snapshot_for_php; + zend_interned_strings_restore = apc_dummy_interned_strings_restore_for_php; + + apc_copy_internal_strings(TSRMLS_C); +} + +void apc_interned_strings_shutdown(TSRMLS_D) +{ + zend_hash_clean(CG(function_table)); + zend_hash_clean(CG(class_table)); + zend_hash_clean(EG(zend_constants)); + + CG(interned_strings_start) = old_interned_strings_start; + CG(interned_strings_end) = old_interned_strings_end; + zend_new_interned_string = old_new_interned_string; + zend_interned_strings_snapshot = old_interned_strings_snapshot; + zend_interned_strings_restore = old_interned_strings_restore; + + DESTROY_LOCK(APCSG(lock)); +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_string.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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: Dmitry Stogov | + +----------------------------------------------------------------------+ + + 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: $ */ + +#ifndef APC_STRING +#define APC_STRING + +#include "apc.h" + +void apc_interned_strings_init(TSRMLS_D); +void apc_interned_strings_shutdown(TSRMLS_D); + +char *apc_new_interned_string(char *arKey, int nKeyLength TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_zend.c @@ -0,0 +1,271 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_zend.c 303426 2010-09-16 16:39:08Z iliaa $ */ + +#include "apc_zend.h" +#include "apc_globals.h" + +/* true global */ +int apc_reserved_offset; + +void* apc_php_malloc(size_t n TSRMLS_DC) +{ + return emalloc(n); +} + +void apc_php_free(void* p TSRMLS_DC) +{ + efree(p); +} + +#ifdef APC_OPCODE_OVERRIDE + +static opcode_handler_t *apc_original_opcode_handlers; +static opcode_handler_t apc_opcode_handlers[APC_OPCODE_HANDLER_COUNT]; + +#define APC_EX_T(offset) (*(temp_variable *)((char*)execute_data->Ts + offset)) + +#ifdef ZEND_ENGINE_2_4 +static zval *apc_get_zval_ptr(zend_uchar op_type, znode_op *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC) +{ + *freeval = NULL; + + switch (op_type) { + case IS_CONST: + return node->zv; + case IS_VAR: + return APC_EX_T(node->var).var.ptr; + case IS_TMP_VAR: + return (*freeval = &APC_EX_T(node->var).tmp_var); + case IS_CV: + { + zval ***ret = &execute_data->CVs[node->var]; + + if (!*ret) { + zend_compiled_variable *cv = &EG(active_op_array)->vars[node->var]; + + if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) { + apc_notice("Undefined variable: %s" TSRMLS_CC, cv->name); + return &EG(uninitialized_zval); + } + } + return **ret; + } + case IS_UNUSED: + default: + return NULL; + } +} +#else +static zval *apc_get_zval_ptr(znode *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC) +{ + *freeval = NULL; + + switch (node->op_type) { + case IS_CONST: + return &(node->u.constant); + case IS_VAR: + return APC_EX_T(node->u.var).var.ptr; + case IS_TMP_VAR: + return (*freeval = &APC_EX_T(node->u.var).tmp_var); +#ifdef ZEND_ENGINE_2_1 + case IS_CV: + { + zval ***ret = &execute_data->CVs[node->u.var]; + + if (!*ret) { + zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var]; + + if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) { + apc_notice("Undefined variable: %s" TSRMLS_CC, cv->name); + return &EG(uninitialized_zval); + } + } + return **ret; + } +#endif + case IS_UNUSED: + default: + return NULL; + } +} +#endif + +static int ZEND_FASTCALL apc_op_ZEND_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS) +{ + APC_ZEND_OPLINE + zval *freeop1 = NULL; + zval *inc_filename = NULL, tmp_inc_filename; + char realpath[MAXPATHLEN]; + php_stream_wrapper *wrapper; + char *path_for_open; + char *full_path = NULL; + int ret = 0; + apc_opflags_t* flags = NULL; + +#ifdef ZEND_ENGINE_2_4 + if (opline->extended_value != ZEND_INCLUDE_ONCE && + opline->extended_value != ZEND_REQUIRE_ONCE) { + return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + inc_filename = apc_get_zval_ptr(opline->op1_type, &opline->op1, &freeop1, execute_data TSRMLS_CC); +#else + if (Z_LVAL(opline->op2.u.constant) != ZEND_INCLUDE_ONCE && + Z_LVAL(opline->op2.u.constant) != ZEND_REQUIRE_ONCE) { + return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + inc_filename = apc_get_zval_ptr(&opline->op1, &freeop1, execute_data TSRMLS_CC); +#endif + + if (Z_TYPE_P(inc_filename) != IS_STRING) { + tmp_inc_filename = *inc_filename; + zval_copy_ctor(&tmp_inc_filename); + convert_to_string(&tmp_inc_filename); + inc_filename = &tmp_inc_filename; + } + + wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(inc_filename), &path_for_open, 0 TSRMLS_CC); + + if (wrapper != &php_plain_files_wrapper || !(IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) || (full_path = expand_filepath(path_for_open, realpath TSRMLS_CC)))) { + /* Fallback to original handler */ + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + if (!full_path) { + full_path = path_for_open; + } + if (zend_hash_exists(&EG(included_files), realpath, strlen(realpath) + 1)) { +#ifdef ZEND_ENGINE_2_4 + if (!(opline->result_type & EXT_TYPE_UNUSED)) { + ALLOC_INIT_ZVAL(APC_EX_T(opline->result.var).var.ptr); + ZVAL_TRUE(APC_EX_T(opline->result.var).var.ptr); + } +#else + if (!(opline->result.u.EA.type & EXT_TYPE_UNUSED)) { + ALLOC_INIT_ZVAL(APC_EX_T(opline->result.u.var).var.ptr); + ZVAL_TRUE(APC_EX_T(opline->result.u.var).var.ptr); + } +#endif + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + if (freeop1) { + zval_dtor(freeop1); + } + execute_data->opline++; + return 0; + } + + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + + if(apc_reserved_offset != -1) { + /* Insanity alert: look into apc_compile.c for why a void** is cast to a apc_opflags_t* */ + flags = (apc_opflags_t*) & (execute_data->op_array->reserved[apc_reserved_offset]); + } + + if(flags && flags->deep_copy == 1) { + /* Since the op array is a local copy, we can cheat our way through the file inclusion by temporarily + * changing the op to a plain require/include, calling its handler and finally restoring the opcode. + */ +#ifdef ZEND_ENGINE_2_4 + opline->extended_value = (opline->extended_value == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE; + ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + opline->extended_value = (opline->extended_value == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE; +#else + Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE; + ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE; +#endif + } else { + ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + return ret; +} + +void apc_zend_init(TSRMLS_D) +{ + zend_extension dummy_ext; + apc_reserved_offset = zend_get_resource_handle(&dummy_ext); + assert(apc_reserved_offset == dummy_ext.resource_number); + assert(apc_reserved_offset != -1); + assert(sizeof(apc_opflags_t) <= sizeof(void*)); + if (!APCG(include_once)) { + /* If we're not overriding the INCLUDE_OR_EVAL handler, then just skip this malarkey */ + return; + } + + memcpy(apc_opcode_handlers, zend_opcode_handlers, sizeof(apc_opcode_handlers)); + + /* 5.0 exposes zend_opcode_handlers differently than 5.1 and later */ +#ifdef ZEND_ENGINE_2_1 + apc_original_opcode_handlers = zend_opcode_handlers; + zend_opcode_handlers = apc_opcode_handlers; +#else + apc_original_opcode_handlers = apc_opcode_handlers; +#endif + + APC_REPLACE_OPCODE(ZEND_INCLUDE_OR_EVAL); +} + +void apc_zend_shutdown(TSRMLS_D) +{ + if (!APCG(include_once)) { + /* Nothing changed, nothing to restore */ + return; + } + +#ifdef ZEND_ENGINE_2_1 + zend_opcode_handlers = apc_original_opcode_handlers; +#else + memcpy(zend_opcode_handlers, apc_original_opcode_handlers, sizeof(apc_opcode_handlers)); +#endif +} + +#else /* Opcode Overrides unavailable */ + +void apc_zend_init(TSRMLS_D) { } +void apc_zend_shutdown(TSRMLS_D) { } + +#endif /* APC_OPCODE_OVERRIDE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/apc_zend.h @@ -0,0 +1,191 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + +----------------------------------------------------------------------+ + + 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: apc_zend.h 303464 2010-09-17 12:19:29Z gopalv $ */ + +#ifndef APC_ZEND_H +#define APC_ZEND_H + +/* Utilities for interfacing with the zend engine */ + +#include "apc.h" +#include "apc_php.h" + +#ifndef Z_REFCOUNT_P +#define Z_REFCOUNT_P(pz) (pz)->refcount +#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) +#endif + +#ifndef Z_SET_REFCOUNT_P +#define Z_SET_REFCOUNT_P(pz, rc) (pz)->refcount = rc +#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) +#endif + +#ifndef Z_ADDREF_P +#define Z_ADDREF_P(pz) (pz)->refcount++ +#define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) +#endif + +#ifndef Z_DELREF_P +#define Z_DELREF_P(pz) (pz)->refcount-- +#define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz)) +#endif + +#ifndef Z_ISREF_P +#define Z_ISREF_P(pz) (pz)->is_ref +#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) +#endif + +#ifndef Z_SET_ISREF_P +#define Z_SET_ISREF_P(pz) (pz)->is_ref = 1 +#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz)) +#endif + +#ifndef Z_UNSET_ISREF_P +#define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0 +#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) +#endif + +#ifndef Z_SET_ISREF_TO_P +#define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = isref +#define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) +#endif + + +extern void* apc_php_malloc(size_t n TSRMLS_DC); +extern void apc_php_free(void* p TSRMLS_DC); + +extern void apc_zend_init(TSRMLS_D); +extern void apc_zend_shutdown(TSRMLS_D); + + +/* offset for apc info in op_array->reserved */ +extern int apc_reserved_offset; + +#ifndef ZEND_VM_KIND_CALL /* Not currently defined by any ZE version */ +# define ZEND_VM_KIND_CALL 1 +#endif + +#ifndef ZEND_VM_KIND /* Indicates PHP < 5.1 */ +# define ZEND_VM_KIND ZEND_VM_KIND_CALL +#endif + +#if defined(ZEND_ENGINE_2) && (ZEND_VM_KIND == ZEND_VM_KIND_CALL) +# define APC_OPCODE_OVERRIDE +#endif + +#ifdef APC_OPCODE_OVERRIDE + +#ifdef ZEND_ENGINE_2_1 +/* Taken from Zend/zend_vm_execute.h */ +#define _CONST_CODE 0 +#define _TMP_CODE 1 +#define _VAR_CODE 2 +#define _UNUSED_CODE 3 +#define _CV_CODE 4 +static inline int _apc_opcode_handler_decode(zend_op *opline) +{ + static const int apc_vm_decode[] = { + _UNUSED_CODE, /* 0 */ + _CONST_CODE, /* 1 = IS_CONST */ + _TMP_CODE, /* 2 = IS_TMP_VAR */ + _UNUSED_CODE, /* 3 */ + _VAR_CODE, /* 4 = IS_VAR */ + _UNUSED_CODE, /* 5 */ + _UNUSED_CODE, /* 6 */ + _UNUSED_CODE, /* 7 */ + _UNUSED_CODE, /* 8 = IS_UNUSED */ + _UNUSED_CODE, /* 9 */ + _UNUSED_CODE, /* 10 */ + _UNUSED_CODE, /* 11 */ + _UNUSED_CODE, /* 12 */ + _UNUSED_CODE, /* 13 */ + _UNUSED_CODE, /* 14 */ + _UNUSED_CODE, /* 15 */ + _CV_CODE /* 16 = IS_CV */ + }; +#ifdef ZEND_ENGINE_2_4 + return (opline->opcode * 25) + (apc_vm_decode[opline->op1_type] * 5) + apc_vm_decode[opline->op2_type]; +#else + return (opline->opcode * 25) + (apc_vm_decode[opline->op1.op_type] * 5) + apc_vm_decode[opline->op2.op_type]; +#endif +} + +# define APC_ZEND_OPLINE zend_op *opline = execute_data->opline; +# define APC_OPCODE_HANDLER_DECODE(opline) _apc_opcode_handler_decode(opline) +# if PHP_MAJOR_VERSION >= 6 +# define APC_OPCODE_HANDLER_COUNT ((25 * 152) + 1) +# elif defined(ZEND_ENGINE_2_4) +# define APC_OPCODE_HANDLER_COUNT ((25 * 157) + 1) /* 3 new opcodes in 5.4? - separate, bind_trais, add_trait */ +# elif PHP_MAJOR_VERSION >= 5 && PHP_MINOR_VERSION >= 3 +# define APC_OPCODE_HANDLER_COUNT ((25 * 154) + 1) /* 3 new opcodes in 5.3 - unused, lambda, jmp_set */ +# else +# define APC_OPCODE_HANDLER_COUNT ((25 * 151) + 1) +# endif +# define APC_REPLACE_OPCODE(opname) { int i; for(i = 0; i < 25; i++) if (zend_opcode_handlers[(opname*25) + i]) zend_opcode_handlers[(opname*25) + i] = apc_op_##opname; } + +#else /* ZE2.0 */ +# define APC_ZEND_ONLINE +# define APC_OPCODE_HANDLER_DECODE(opline) (opline->opcode) +# define APC_OPCODE_HANDLER_COUNT 512 +# define APC_REPLACE_OPCODE(opname) zend_opcode_handlers[opname] = apc_op_##opname; +#endif + +#ifndef ZEND_FASTCALL /* Added in ZE2.3.0 */ +#define ZEND_FASTCALL +#endif + +/* Added in ZE2.3.0 */ +#ifndef zend_parse_parameters_none +# define zend_parse_parameters_none() zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") +#endif + + +#endif /* APC_OPCODE_OVERRIDE */ + +#ifdef ZEND_ENGINE_2_4 +# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len +# define ZEND_CE_BUILTIN_FUNCTIONS(ce) (ce)->info.internal.builtin_functions +#else +# define ZEND_CE_FILENAME(ce) (ce)->filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->doc_comment_len +# define ZEND_CE_BUILTIN_FUNCTIONS(ce) (ce)->builtin_functions +#endif + +#endif /* APC_ZEND_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/CHANGELOG @@ -0,0 +1,249 @@ + +3.1.2 : 2008-12-12 + +- pecl package.xml/build fixes (bjori) + +3.1.1 : 2008-12-12 + +- PHP4 compatibilty break +- apc_pool allocator (Gopal) +- doubly-linked sma allocator (Shire) +- php 5.3 gc compatibility (Gopal) +- APCIterator for easy access (Shire) +- apc_delete_file (Shire) +- apc_inc/apc_dec/apc_cas functions (Shire) +- apc.canonicalize (Gopal) +- apc.preload_path (Gopal) +- apc.rfc1867_ttl (Shire) +- apc.file_md5 (Shire) +- consolidate locking macros (Shire) +- remove futex/TSRM locks (Shire) +- non-blocking semaphore locks (Shire) +- zval* object rework (Gopal) + +3.0.19: 2008-05-14 +- Safe-mode and fast-cgi fixes +- Fix double-free of builtin_functions +- php 5.3 fixes + +3.0.18: 2008-03-29 +- Revert apc_expunge_cb bug-fix +- Misc memleaks + +3.0.17: 2008-03-26 +- Crash fixes +- Fix apc_add() cache expunge bug (Rasmus) +- Added parameter to apc_fetch to determine success/failure when fetching booleans (shire) +- Fix misc. memleaks (shire) + +3.0.16: 2007-12-26 +- Fix for longstanding cache-full crash (Christian Seiler) + http://news.php.net/php.pecl.dev/4951 for the details +- Added optional shm unmap on a fatal signal feature (Lucas Nealan) +- Added PTHREAD_MUTEX_ADAPTIVE_NP option pthread locks (Paul Saab) +- Minor cleanups (Lucas Nealan) +- Added configure option to enable apc_cache_info('filehits') (Shire) + +3.0.15: 2007-10-18 +- Eliminate a per-request time() syscall (Rasmus) +- Added rfc1867 prefix, name, and freq ini options (Shire) +- Allow deletion of individual user cache entries via apc.php (Sara) +- Fix overzealous cleanup during RSHUTDOWN (Gopal) +- Fix memory alignment and locking issues (Gopal) +- Make apc_compile insert/replace entries (Shire) +- Make mixed inheritance recompile & cache afresh (Gopal) +- Make nostat mode search include_path for canonicalization (Gopal) +- ZTS & other compile fixes (Gopal, Edin, Shire) + +3.0.14: 2007-03-21 +- Build fix (Shire) +- Don't hook the upload hook if APC is disabled (Rasmus) +- Local shadow cache support (Gopal) +- Avoid uneccessary loops over op_arrays for "known" auto-globals (Gopal) +- Fix apc_add() to overwrite timed out user entries (Rasmus) +- Fix double inclusion of files with conditional classes in php4 (Gopal) +- Allocator fixes to reduce fragmentation (Gopal) + +3.0.13: 2007-02-24 +- File upload progress (Rasmus) +- Pthread mutex and spin locks (Shire) +- Recursive zval support for apc_fetch/_store (Shire, Gopal) +- apc.stat_ctime flag for ctime checks (Rasmus) +- Multiple key fetches with apc_fetch (Shire) +- Canary checks for shm memory deallocation (Gopal) +- Add hooks for external optimizer (Shire) +- Obsolete and remove apc optimizer (Gopal) +- APC info changes - cache insert rate, hit and miss rates (Shire) +- Fix apc_load_constants (Gopal) +- Rewrite dump opcode code to use vld (Gopal) +- Use apc_[ewn]print functions for error reporting (Shire) +- Auto global fixes and refactoring (Gopal, Shire) +- Fix memory leaks in object serialization (Ilia) +- Memory cleanup code for destructor order (Gopal) +- Win32 build fixes (Ilia, Wez) +- ZTS and Php 4 build fixes (Bjori) +- Add apc_add() function (Rasmus) +- Add optional limited flag to apc_sma_info() (Rasmus) + +3.0.12p2: 2006-09-05 +- Package version up + +3.0,12p1: 2006-09-05 +- PHP4 build fixes + +3.0.12: 2006-09-05 +- PHP 5.2 compatibility (Gopal) +- TSRM fixes (Gopal) +- Add extra flags to op_array->reserved to improve op array + processing code (Gopal) +- Fix crashes in optimizer and cli mode (Ilia) +- Optimizer fixes for PHP5 (Ilia, Gopal) +- Allow multiple inclusions of a file with a dynamic class (Gopal) +- Php 4 function table and properties fixes (Gopal) +- Fix memory leaks in apc_cache_info (Gopal) + +3.0.11: 2006-08-16 +- Made --enable-apc-mmap the default compile option (for real this time) +- Add an optional flag to apc_cache_info() and some apc.php tweaks to make it + only fetch header information to make it useful when you have tens of + thousands of entries. (Brian Shire) +- 64-bit fixes (George) +- Don't mix Full Path and Inode keys (George) +- Override ZEND_INCLUDE_OR_EVAL opcode (when possible) to speed up use of + require_once() and include_once() statements. (Sara) +- Add a non-blocking write_lock for cache inserts. This is a better approach + to prevent cache slams and deprecates the slam_defense setting. (Rasmus) +- A bit of work on the optimizer. (Sara) +- Various memory issues resolved. (Gopal) + +3.0.10: 2006-03-11 +- Add apc.stat ini flag which defaults to 1. If set to 0, the main script and any fullpath + includes will not be stat'ed for any changes. You will have to restart the server if you + change anything. This mode increases performance quite a bit, especially if you have a + lot of includes. + +- Get rid of the lock safety net hack I added in 3.0.9. It seems to cause more problems + than it solves. I'll need to revisit locking and signal handling at some point soon. + +3.0.9: 2006-03-04 +- Eliminate rand() call when slam_defense is not set (Rasmus) +- Fix for __isset problem (Gopal) +- Rewrite allocator from a "best fit" to a "next fit" algorithm (Rasmus) +- Added a Cache Full counter so we have an idea how many times the segment has filled up causing an expunge (Rasmus) +- Report back the correct number of available bytes in the segment instead of the allocated bytes. (Rasmus) +- Add cache busy flag which is set when an expunge is underway (Rasmus) +- Add automatic serialization of objects in apc_store() (Marcus) +- 64-bit .ini flag fix (Rasmus) +- Static members fix (Gopal) +- sma_cleanup() mem leak fix (Rasmus) +- Fix for http://pecl.php.net/bugs/5311 (Rasmus) +- Fix autoglobals JIT bug (Gopal) +- Fix instance bug (Gopal) +- Add a lock cleanup safety net to request shutdown (Rasmus) +- Fix apc.slam_defense edge-case bug (Rasmus) +- User entry memory usage tracking support (Ilia) +- Allow keys used in apc_store/apc_fetch/apc_delete to be binary safe and prevent conflicts between keys that are found at the start of other keys. (Ilia) + +3.0.8: 2005-08-24 +Fix invalid free in globals destructor introduced in 3.0.7 (Rasmus) +Cache corruption fix in cache-full cleanup code (Gopal) + +3.0.7: 2005-08-16 +- Fix to apc.php to show final segment in frag chart. (Ilia) +- A couple of win32 fixes. (Frank) +- Add apc.enable_cli ini directive. (Rasmus) +- Add test cases. (Marcus) +- Fix apc_define_constants() bug - http://pecl.php.net/bugs/5084 (Rasmus) +- Simplify user cache handling by removing the user_cache_stack (Rasmus) +- Fix apc_fetch() memory corruption (Andrei,Rasmus) +- Added apc.max_file_size INI setting that allows exclusion of large files from being cached. Default file size limit, 1 megabyte. (Ilia) + +3.0.6: 2005-07-30 +- Added apc.php to package.xml file. +- Track per-entry memory usage. (Val) +- Various apc.php fixes and enhancements. (Ralf, Ilia, Rasmus) +- fcntl locking robustness fixes. (Rasmus) +- Shared read-locks where possible. (Rasmus) +- Added file_update_protection configuration parameter. (Rasmus) +- Windows ZTS fixes (Frank) + +3.0.5: 2005-07-27 +- Make it easier for sapis that only populate file_handle->filename to use APC. (Rasmus) +- Support extensions such as bcompiler that need to hook into compile_file. (Val) +- Ralf Becker's apcgui code has now become the default apc.php status page. (Ralf, Rasmus, Ilia) +- Segfault in cache cleanup code (Ilia, Rasmus) + +3.0.4: 2005-07-18 +- Add win32 support (Edin ) +- Add --with-apxs switch to work around problem when loading APC into Apache binary compiled with LFS switches (Rasmus) +- A couple of other minor fixes + +3.0.3: 2005-07-05 +- Fix compile problem against PHP 5.0.x + +3.0.2: 2005-07-05 +- Better shm error message + +3.0.1: 2005-07-05 +- PHP4 build fix + +3.0: 2005-06-23 +- PHP 5.1 support (Arun, Gopal, Rasmus) +- Major Inheritance bug fix (Arun, Gopal) + +2.0: 2003-02-10 +- ground-up rewrite sharing none of the original source code (djc) + +1.0.10: +- merge mmap / shm code to be in one file, module supports both modes now [mpb 2001-05-15] +- added apc.mode config parameter [mpb 2001-05-15] NOTE: You'll have to add + this parameter to your php.ini file to activate apc shm or mmap caching +- generic source cleanup (missing includes, PATH_MAX usage etc) [mpb + 2001-05-15] +- fixed: realpath return result checking in generate_key [mpb 2001-05-15] +- updated: gui updated (extras/apc_gui-1.0.2.tar.gz) +- experminental 'fast' cache-retrieval [djc 2001-05-20] +- fixed regex support [gws 2001-05-16] +- enhanced reader-writer lock support [rg 2001-05-07] + +1.0.9: +- fixed (?) memory alignment bug on 64 bit archiecures +- added many cache visibiliy functions +- added opional fcntl locks under shm version +- numerous bug fixes + +1.0.8: +- added ability to detect and decompile compiled files placed as 'source' + [gws,dw 2001-01-30] +- fixed apc_rstat bug [gws 2001-01-29] +- added hack to support included urls [gws 2001-01-30] +- fixed apc_cache_index [mb 2001-01-31] +- added multiple regex support [gs 2001-02-03] +- added apc_cache_info [mb,gs 2001-02-03] + +1.0.7: +- partially fixed for Solaris [gws 2001-01-29] +- fixed mtime support for relative includes [gws 2001-01-29] +- code cleanup [yg,ta,gws 2001-01-29] + +1.0.6: +- support for mtime in mmap [yg,gws 2001-01-27] +- fixed indexed-array initialization bug [djc,gws 2001-01-27] + +1.0.5: +- support for relative include paths [djc,gws 2001-01-19] +- class member array support fixed [djc 2001-01-18] +- added apc_cache_index [gws 2001-01-18] + +1.0.4: +- support for class hierarchies greater than two levels deep [djc 2001-01-17] + +1.0.3: +- fixed support for class inheritance [djc 2001-01-16] + +1.0.2: +- support for inherited classes [gws 2001-01-15] +- support for intialization of class variables and objects [gws 2001-01-13] + +1.0.1: +- added optional file modification time check [djc 2001-01-12] --- /dev/null +++ b/ext/apc/config.m4 @@ -0,0 +1,246 @@ +dnl +dnl $Id: config.m4 304101 2010-10-05 14:37:36Z kalle $ +dnl + +PHP_ARG_ENABLE(apc, whether to enable APC support, +[ --enable-apc Enable APC support]) + +AC_ARG_ENABLE(apc-debug, +[ --enable-apc-debug Enable APC debugging], +[ + PHP_APC_DEBUG=yes +], +[ + PHP_APC_DEBUG=no +]) + +AC_MSG_CHECKING(whether we should enable cache request file info) +AC_ARG_ENABLE(apc-filehits, +[ --enable-apc-filehits Enable per request file info about files used from the APC cache (ie: apc_cache_info('filehits')) ], +[ + PHP_APC_FILEHITS=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_FILEHITS=no + AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(whether we should use mmap) +AC_ARG_ENABLE(apc-mmap, +[ --disable-apc-mmap + Disable mmap support and use IPC shm instead], +[ + PHP_APC_MMAP=$enableval + AC_MSG_RESULT($enableval) +], [ + PHP_APC_MMAP=yes + AC_MSG_RESULT(yes) +]) + +AC_MSG_CHECKING(whether we should use semaphore locking instead of fcntl) +AC_ARG_ENABLE(apc-sem, +[ --enable-apc-sem + Enable semaphore locks instead of fcntl], +[ + PHP_APC_SEM=$enableval + AC_MSG_RESULT($enableval) +], [ + PHP_APC_SEM=no + AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(whether we should use pthread mutex locking) +AC_ARG_ENABLE(apc-pthreadmutex, +[ --disable-apc-pthreadmutex + Disable pthread mutex locking ], +[ + PHP_APC_PTHREADMUTEX=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_PTHREADMUTEX=yes + AC_MSG_RESULT(yes) +]) + +if test "$PHP_APC_PTHREADMUTEX" != "no"; then + orig_LIBS="$LIBS" + LIBS="$LIBS -lpthread" + AC_TRY_RUN( + [ + #include + #include + main() { + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + + if(pthread_mutexattr_init(&attr)) { + puts("Unable to initialize pthread attributes (pthread_mutexattr_init)."); + return -1; + } + if(pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)) { + puts("Unable to set PTHREAD_PROCESS_SHARED (pthread_mutexattr_setpshared), your system may not support shared mutex's."); + return -1; + } + if(pthread_mutex_init(&mutex, &attr)) { + puts("Unable to initialize the mutex (pthread_mutex_init)."); + return -1; + } + if(pthread_mutexattr_destroy(&attr)) { + puts("Unable to destroy mutex attributes (pthread_mutexattr_destroy)."); + return -1; + } + if(pthread_mutex_destroy(&mutex)) { + puts("Unable to destroy mutex (pthread_mutex_destroy)."); + return -1; + } + + puts("pthread mutex's are supported!"); + return 0; + } + ], + [ dnl -Success- + PHP_ADD_LIBRARY(pthread) + ], + [ dnl -Failure- + AC_MSG_WARN([It doesn't appear that pthread mutex's are supported on your system]) + PHP_APC_PTHREADMUTEX=no + ], + [ + PHP_ADD_LIBRARY(pthread) + ] + ) + LIBS="$orig_LIBS" +fi + +AC_MSG_CHECKING(whether we should use spin locks) +AC_ARG_ENABLE(apc-spinlocks, +[ --enable-apc-spinlocks + Enable spin locks EXPERIMENTAL ], +[ + PHP_APC_SPINLOCKS=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_SPINLOCKS=no + AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(whether we should enable memory protection) +AC_ARG_ENABLE(apc-memprotect, +[ --enable-apc-memprotect + Enable mmap/shm memory protection], +[ + PHP_APC_MEMPROTECT=$enableval + AC_MSG_RESULT($enableval) +], [ + PHP_APC_MEMPROTECT=no + AC_MSG_RESULT(no) +]) + +if test "$PHP_APC" != "no"; then + test "$PHP_APC_MMAP" != "no" && AC_DEFINE(APC_MMAP, 1, [ ]) + test "$PHP_APC_FILEHITS" != "no" && AC_DEFINE(APC_FILEHITS, 1, [ ]) + + if test "$PHP_APC_DEBUG" != "no"; then + AC_DEFINE(__DEBUG_APC__, 1, [ ]) + fi + + if test "$PHP_APC_SEM" != "no"; then + AC_DEFINE(APC_SEM_LOCKS, 1, [ ]) + elif test "$PHP_APC_SPINLOCKS" != "no"; then + AC_DEFINE(APC_SPIN_LOCKS, 1, [ ]) + elif test "$PHP_APC_PTHREADMUTEX" != "no"; then + AC_DEFINE(APC_PTHREADMUTEX_LOCKS, 1, [ ]) + else + AC_DEFINE(APC_FCNTL_LOCKS, 1, [ ]) + fi + + if test "$PHP_APC_MEMPROTECT" != "no"; then + AC_DEFINE(APC_MEMPROTECT, 1, [ shm/mmap memory protection ]) + fi + + AC_CACHE_CHECK(for zend_set_lookup_function_hook, php_cv_zend_set_lookup_function_hook, + [ + orig_cflags=$CFLAGS + CFLAGS="$INCLUDES $EXTRA_INCLUDES" + AC_TRY_COMPILE([ +#include "main/php.h" +#include "Zend/zend_API.h" + ], [#ifndef zend_set_lookup_function_hook + (void) zend_set_lookup_function_hook; +#endif], [ + php_cv_zend_set_lookup_function_hook=yes + ],[ + php_cv_zend_set_lookup_function_hook=no + ]) + CFLAGS=$orig_cflags + ]) + if test "$php_cv_zend_set_lookup_function_hook" = "yes"; then + AC_DEFINE(APC_HAVE_LOOKUP_HOOKS, 1, [ ]) + else + AC_DEFINE(APC_HAVE_LOOKUP_HOOKS, 0, [ ]) + fi + + AC_CHECK_FUNCS(sigaction) + AC_CACHE_CHECK(for union semun, php_cv_semun, + [ + AC_TRY_COMPILE([ +#include +#include +#include + ], [union semun x; x.val=1], [ + php_cv_semun=yes + ],[ + php_cv_semun=no + ]) + ]) + if test "$php_cv_semun" = "yes"; then + AC_DEFINE(HAVE_SEMUN, 1, [ ]) + else + AC_DEFINE(HAVE_SEMUN, 0, [ ]) + fi + + AC_MSG_CHECKING(whether we should enable valgrind support) + AC_ARG_ENABLE(valgrind-checks, + [ --disable-valgrind-checks + Disable valgrind based memory checks], + [ + PHP_APC_VALGRIND=$enableval + AC_MSG_RESULT($enableval) + ], [ + PHP_APC_VALGRIND=yes + AC_MSG_RESULT(yes) + AC_CHECK_HEADER(valgrind/memcheck.h, + [AC_DEFINE([HAVE_VALGRIND_MEMCHECK_H],1, [enable valgrind memchecks])]) + ]) + + apc_sources="apc.c php_apc.c \ + apc_cache.c \ + apc_compile.c \ + apc_debug.c \ + apc_fcntl.c \ + apc_main.c \ + apc_mmap.c \ + apc_sem.c \ + apc_shm.c \ + apc_pthreadmutex.c \ + apc_spin.c \ + pgsql_s_lock.c \ + apc_sma.c \ + apc_stack.c \ + apc_zend.c \ + apc_rfc1867.c \ + apc_signal.c \ + apc_pool.c \ + apc_iterator.c \ + apc_bin.c \ + apc_string.c " + + PHP_CHECK_LIBRARY(rt, shm_open, [PHP_ADD_LIBRARY(rt,,APC_SHARED_LIBADD)]) + PHP_NEW_EXTENSION(apc, $apc_sources, $ext_shared,, \\$(APC_CFLAGS)) + PHP_SUBST(APC_SHARED_LIBADD) + PHP_SUBST(APC_CFLAGS) + AC_DEFINE(HAVE_APC, 1, [ ]) +fi + --- /dev/null +++ b/ext/apc/config.w32 @@ -0,0 +1,48 @@ +// $Id: config.w32 305410 2010-11-16 16:47:18Z pajoye $ +// vim:ft=javascript + + +ARG_ENABLE('apc', 'Whether to enable APC support', 'no'); +ARG_ENABLE('apc-debug', 'Whether to enable APC debugging', 'no'); +ARG_ENABLE('apc-filehits', 'Whether to enable cache request file info', 'no'); +ARG_ENABLE('apc-spinlocks', 'Whether to use spin locks (experimental)', 'no'); +ARG_ENABLE('apc-memprotect', 'Whether to enable memory protection (experimental)', 'no'); + +if(PHP_APC != 'no') +{ + var apc_sources = 'apc.c php_apc.c apc_cache.c apc_compile.c apc_debug.c ' + + 'apc_fcntl_win32.c apc_iterator.c apc_main.c apc_shm.c ' + + 'apc_sma.c apc_stack.c apc_rfc1867.c apc_zend.c apc_pool.c ' + + 'apc_bin.c apc_string.c'; + + if(PHP_APC_DEBUG != 'no') + { + ADD_FLAG('CFLAGS_APC', '/D __DEBUG_APC__=1'); + } + + if(PHP_APC_FILEHITS != 'no') + { + AC_DEFINE('APC_FILEHITS', 1); + } + + if(PHP_APC_MEMPROTECT != 'no') + { + AC_DEFINE('APC_MEMPROTECT', 1); + } + + if(PHP_APC_SPINLOCKS != 'no') + { + AC_DEFINE('APC_SPIN_LOCKS', 1); + ADD_FLAG('CFLAGS_APC', '/D WIN32_ONLY_COMPILER=1'); + + apc_sources += ' apc_spin.c pgsql_s_lock.c'; + } + else + { + AC_DEFINE('APC_FCNTL_LOCKS', 1); + } + + AC_DEFINE('HAVE_APC', 1); + + EXTENSION('apc', apc_sources); +} \ No newline at end of file --- /dev/null +++ b/ext/apc/INSTALL @@ -0,0 +1,400 @@ +Installation Instructions for APC +--------------------------------- + +This version of APC should work on PHP 4.3.0 - 4.4.x and +5.1.0 - 5.2.x. Yes, that means PHP 5.0.x is no longer +supported. Upgrade to PHP 5.1.x or 5.2.x and you will +notice all sorts of performance increases. + +CVS Instructions +---------------- +Building from CVS can be done like this: + + svn co http://svn.php.net/repository/pecl/apc/trunk apc + cd apc + phpize + ./configure --with-php-config=/usr/local/php/bin/php-config + make + export TEST_PHP_ARGS='-n' + make test + make install + +Suggested Configuration (in your php.ini file) +---------------------------------------------- + extension=apc.so + apc.enabled=1 + apc.shm_size=128 + apc.ttl=7200 + apc.user_ttl=7200 + apc.enable_cli=1 + +These are fully described at the bottom of this file. + ++---------------------+ +| QUICK INSTALL (DSO) | ++---------------------+ + +These instructions assume your PHP installation is located in /usr/local/php and you +want Apache optimizations (--with-apxs). + +$ gunzip -c apc_x.y.tar.gz | tar xf - +$ cd apc_x.y +$ /usr/local/php/bin/phpize +$ ./configure --with-php-config=/usr/local/php/bin/php-config +$ make +$ make install + +You will probably need to run the final command (make install) as root. + +The above sequence of commands will install a .so file in your PHP +installation extension directory. The output of make install should display +that path to the screen. + +Next you must edit your php.ini file, which is normally located in +/usr/local/php/lib/php.ini, and add the following line: + + extension="apc.so" + +Replace "/path/to/php/extensions" with whatever path was displayed when you +ran make install above. + +Then restart your web server and consult the output of phpinfo(). If there is +an informational section for APC, the installation was successful. + ++------------------------+ +| QUICK INSTALL (Static) | ++------------------------+ + +APC will not successfully compile on all systems as a DSO. If you run into +problems using the DSO quick install, you can try to compile it statically +into PHP. (The DSO install is recommended, though.) + +These instructions assume the current directory is the root of the PHP source +tree, and that you have already configured PHP by running its bundled +configure script. + +$ cd ext +$ gunzip -c apc_x.y.tar.gz | tar xf - +$ cd .. +$ ./buildconf +$ ./config.nice +$ make +$ make install + +Once this is complete, simply restart your web server. You do not need to +modify your php.ini file to enable APC. + ++-----------------+ +| VERBOSE INSTALL | ++-----------------+ + +These instructions assume your PHP installation is located in /usr/local/php. + +1. Unpack your distribution file. + + You will have downloaded a file named something like apc_x.y.tar.gz. + Unzip this file with a command like + + gunzip apc_x.y.tar.gz + + Next you have to untar it with + + tar xvf apc_x.y.tar + + This will create an apc_x.y directory. cd into this new directory: + + cd apc_x.y + +2. Run phpize. + + phpize is a script that should have been installed with PHP, and is + normally located in /usr/local/php/bin assuming you installed PHP in + /usr/local/php. (If you do not have the phpize script, you must reinstall + PHP and be sure not to disable PEAR.) + + Run the phpize command: + + /usr/local/php/bin/phpize + + Its output should resemble this: + + autoheader: `config.h.in' is created + You should update your `aclocal.m4' by running aclocal. + Configuring for: + PHP Api Version: 20020918 + Zend Module Api No: 20020429 + Zend Extension Api No: 20021010 + + phpize should create a configure script in the current directory. If you + get errors instead, you might be missing some required development tools, + such as autoconf or libtool. You can try downloading the latest versions + of those tools and running phpize again. + +3. Run the configure script. + + phpize creates a configure script. The only option you need to specify is + the location of your php-config script: + + ./configure --enable-apc + + php-config should be located in the same directory as phpize. + + If you prefer to use mmap instead of the default IPC shared memory support, + add --enable-apc-mmap to your configure line. + + If you prefer to use sysv IPC semaphores over the safer fcntl() locks, add + --enable-sem to your configure line. If you don't have a problem + with your server segaulting, or any other unnatural accumulation of + semaphores on your system, the semaphore based locking is slightly faster. + +4. Compile and install the files. Simply type: make install + + (You may need to be root in order to install) + + If you encounter errors from libtool or gcc during this step, please + contact the project maintainer (dcowgill@php.net). + +5. Edit your php.ini + + make install should have printed a line resembling the following: + + Installing shared extensions: /path/to/extension/ + + Copy the path /path/to/extension/ and add the following line to your + php.ini file (normally located in /usr/local/php/lib/php.ini): + + extension="apc.so" + + If you don't have a php.ini file in that location, you can create it now. + +6. Restart the web server and test the installation. + + Restart your web server now (for apache, it's apachectl restart) and + create a small test PHP file in your document root. The file should + contain just the following line: + + + + Request that file in a web browser. If there is an entry for APC in the + list of installed modules, the installation was successful. + + If APC is not listed, consult your web server error log. If it contains an + error message saying that it can't load the APC extension, your system + might not be able to load shared libraries created with PHP's build + system. One alternative would be to compile APC statically into PHP. See + the Quick Install (Static) instructions above. + + You should consult your error log anyway to see if APC generated any + errors. On BSD-based platforms, it is typical for APC to be unable to + allocate the default-sized shared memory segment. See below for hints on + raising your system's shared memory limitations. + ++-----------------+ +| CONFIGURING APC | ++-----------------+ + +Although the default APC settings are fine for many installations, serious +users should consider tuning the following parameters: + + OPTION DESCRIPTION + ------------------ -------------------------------------------------- + apc.enabled This can be set to 0 to disable APC. This is + primarily useful when APC is statically compiled + into PHP, since there is no other way to disable + it (when compiled as a DSO, the zend_extension + line can just be commented-out). + (Default: 1) + + apc.shm_segments The number of shared memory segments to allocate + for the compiler cache. If APC is running out of + shared memory but you have already set + apc.shm_size as high as your system allows, you + can try raising this value. Setting this to a + value other than 1 has no effect in mmap mode + since mmap'ed shm segments don't have size limits. + (Default: 1) + + apc.shm_size The size of each shared memory segment in MB. + By default, some systems (including most BSD + variants) have very low limits on the size of a + shared memory segment. + (Default: 30) + + apc.optimization This option has been deprecated. + (Default: 0) + + apc.num_files_hint A "hint" about the number of distinct source files + that will be included or requested on your web + server. Set to zero or omit if you're not sure; + this setting is mainly useful for sites that have + many thousands of source files. + (Default: 1000) + + apc.user_entries_hint Just like num_files_hint, a "hint" about the number + of distinct user cache variables to store. + Set to zero or omit if you're not sure; + (Default: 4096) + + apc.ttl The number of seconds a cache entry is allowed to + idle in a slot in case this cache entry slot is + needed by another entry. Leaving this at zero + means that your cache could potentially fill up + with stale entries while newer entries won't be + cached. + (Default: 0) + + apc.user_ttl The number of seconds a user cache entry is allowed + to idle in a slot in case this cache entry slot is + needed by another entry. Leaving this at zero + means that your cache could potentially fill up + with stale entries while newer entries won't be + cached. + (Default: 0) + + + apc.gc_ttl The number of seconds that a cache entry may + remain on the garbage-collection list. This value + provides a failsafe in the event that a server + process dies while executing a cached source file; + if that source file is modified, the memory + allocated for the old version will not be + reclaimed until this TTL reached. Set to zero to + disable this feature. + (Default: 3600) + + apc.cache_by_default On by default, but can be set to off and used in + conjunction with positive apc.filters so that files + are only cached if matched by a positive filter. + (Default: On) + + apc.filters A comma-separated list of POSIX extended regular + expressions. If any pattern matches the source + filename, the file will not be cached. Note that + the filename used for matching is the one passed + to include/require, not the absolute path. If the + first character of the expression is a + then the + expression will be additive in the sense that any + files matched by the expression will be cached, and + if the first character is a - then anything matched + will not be cached. The - case is the default, so + it can be left off. + (Default: "") + + apc.mmap_file_mask If compiled with MMAP support by using --enable-mmap + this is the mktemp-style file_mask to pass to the + mmap module for determing whether your mmap'ed memory + region is going to be file-backed or shared memory + backed. For straight file-backed mmap, set it to + something like /tmp/apc.XXXXXX (exactly 6 X's). + To use POSIX-style shm_open/mmap put a ".shm" + somewhere in your mask. eg. "/apc.shm.XXXXXX" + You can also set it to "/dev/zero" to use your + kernel's /dev/zero interface to anonymous mmap'ed + memory. Leaving it undefined will force an + anonymous mmap. + (Default: "") + + apc.slam_defense ** DEPRECATED - Use apc.write_lock instead ** + On very busy servers whenever you start the server or + modify files you can create a race of many processes + all trying to cache the same file at the same time. + This option sets the percentage of processes that will + skip trying to cache an uncached file. Or think of it + as the probability of a single process to skip caching. + For example, setting this to 75 would mean that there is + a 75% chance that the process will not cache an uncached + file. So the higher the setting the greater the defense + against cache slams. Setting this to 0 disables this + feature. + (Default: 0) + + apc.file_update_protection + When you modify a file on a live web server you really + should do so in an atomic manner. That is, write to a + temporary file and rename (mv) the file into its permanent + position when it is ready. Many text editors, cp, tar and + other such programs don't do this. This means that there + is a chance that a file is accessed (and cached) while it + is still being written to. This file_update_protection + setting puts a delay on caching brand new files. The + default is 2 seconds which means that if the modification + timestamp (mtime) on a file shows that it is less than 2 + seconds old when it is accessed, it will not be cached. + The unfortunate person who accessed this half-written file + will still see weirdness, but at least it won't persist. + If you are certain you always atomically update your files + by using something like rsync which does this correctly, you + can turn this protection off by setting it to 0. If you + have a system that is flooded with io causing some update + procedure to take longer than 2 seconds, you may want to + increase this a bit. + (Default: 2) + + apc.enable_cli Mostly for testing and debugging. Setting this enables APC + for the CLI version of PHP. Normally you wouldn't want to + create, populate and tear down the APC cache on every CLI + request, but for various test scenarios it is handy to be + able to enable APC for the CLI version of APC easily. + (Default: 0) + + apc.max_file_size Prevents large files from being cached. + (Default: 1M) + + apc.stat Whether to stat the main script file and the fullpath + includes. If you turn this off you will need to restart + your server in order to update scripts. + (Default: 1) + + apc.canonicalize Whether to canonicalize paths in stat=0 mode or + fall back to stat behaviour if set to 0 + (Default: 0) + + apc.write_lock On busy servers when you first start up the server, or when + many files are modified, you can end up with all your processes + trying to compile and cache the same files. With write_lock + enabled, only one process at a time will try to compile an + uncached script while the other processes will run uncached + instead of sitting around waiting on a lock. + (Default: 1) + + apc.report_autofilter Logs any scripts that were automatically excluded from being + cached due to early/late binding issues. + (Default: 0) + + apc.rfc1867 RFC1867 File Upload Progress hook handler is only available + if you compiled APC against PHP 5.2.0 or later. When enabled + any file uploads which includes a field called + APC_UPLOAD_PROGRESS before the file field in an upload form + will cause APC to automatically create an upload_ + user cache entry where is the value of the + APC_UPLOAD_PROGRESS form entry. + + Note that the file upload tracking is not threadsafe at this + point, so new uploads that happen while a previous one is + still going will disable the tracking for the previous. + (Default: 0) + + apc.rfc1867_prefix Key prefix to use for the user cache entry generated by + rfc1867 upload progress functionality. + (Default: "upload_") + + apc.rfc1867_name Specify the hidden form entry name that activates APC upload + progress and specifies the user cache key suffix. + (Default: "APC_UPLOAD_PROGRESS") + + apc.rfc1867_freq The frequency that updates should be made to the user cache + entry for upload progress. This can take the form of a + percentage of the total file size or a size in bytes + optionally suffixed with 'k', 'm', or 'g' for kilobytes, + megabytes, or gigabytes respectively (case insensitive). + A setting of 0 updates as often as possible, which may cause + slower uploads. + (Default: 0) + + apc.localcache ** REMOVED + apc.localcache.size ** REMOVED + + apc.include_once_override + Optimize include_once and require_once calls and avoid the + expensive system calls used. + (Default: 0) --- /dev/null +++ b/ext/apc/LICENSE @@ -0,0 +1,68 @@ +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. --- /dev/null +++ b/ext/apc/NOTICE @@ -0,0 +1,43 @@ +This is the NOTICE file that holds acknowledgements and stuff. + +The Alternative PHP Cache (APC) is a free and open opcode cache for PHP. +This extension is being released under the PHP License for complete compliance +with PHP and to encourage wide-spread use. It is our intention that this +project be kept open source and that all commercial spin-offs contribute their +modifications back into the public source-tree. + +Creators: + Daniel Cowgill + George Schlossnagle + +PHP5 support and major features by: + Arun C. Murthy + Gopal Vijayaraghavan + Rasmus Lerdorf + +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. + +We would like to thank Community Connect Inc. and Yahoo! Inc. for supporting +this project and providing a challenging and stimulating environment in +which exciting projects can happen. + +Contributors: + Mike Bretz bug fixes, GUI, and lots of work + Ricardo Galli changed read-write locks to prefer readers + Yann Grossel bug fixes + Thies Arntzen bug fixes + Sara Golemon optimizer work + +Special Thanks: + Florian Baumert help debugging phplib problems + Thomas Duffey help debugging inheritance issues + Vibol Hou help debugging phplib problems + Angel Li diffs for ANSI comment compliance + Christian Rishøj help debugging phplib problems + Sascha Schumann memory error bug fix --- /dev/null +++ b/ext/apc/package.xml @@ -0,0 +1,827 @@ + + + APC + pecl.php.net + Alternative PHP Cache + APC is a free, open, and robust framework for caching and optimizing PHP intermediate code. + + Daniel Cowgill + dcowgill + dan@mail.communityconnect.com + no + + + George Schlossnagle + gschlossnagle + george@omniti.com + no + + + Rasmus Lerdorf + rasmus + rasmus@php.net + yes + + + Gopal Vijayaraghavan + gopalv + gopalv@php.net + yes + + + Edin Kadribasic + edink + edink@emini.dk + no + + + Ilia Alshanetsky + iliaa + ilia@prohost.org + yes + + + Marcus Börger + helly + helly@php.net + no + + + Sara Golemon + pollita + pollita@php.net + no + + + Brian Shire + shire + shire@php.net + yes + + + Kalle Sommer Nielsen + kalle + kalle@php.net + yes + + + Pierre Joye + pajoye + pierre@php.net + yes + + 2010-11-30 + + + 3.1.6 + 3.1.0 + + + stable + stable + + PHP License + +- make slam_defense a little more optimistic, allow a thread/process to write to cache in a loop +- ensure realpaths hit the realpath_cache, in no-stat mode +- prevent memory starvation, nuke all caches when expunging just one doesn't work +- fix uploadprogress keylength issues (NUL is part of keylen, pecl bug #20016) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.1.0 + + + 1.4.0 + + + + apc + + + + + + + + stable + stable + + + 2.0.0 + 2.0.0 + + 2003-07-01 + +Complete rework. + + + + + stable + stable + + + 2.0.1 + 2.0.0 + + 2003-07-01 + +Win32 support added. + + + + + stable + stable + + + 2.0.2 + 2.0.0 + + 2004-03-12 + +Fixed non-existant class bug. + + + + + stable + stable + + + 3.0.0 + 3.0.0 + + 2005-07-05 + +PHP-5.1 Support and numerous fixes + + + + + stable + stable + + + 3.0.1 + 3.0.0 + + 2005-07-05 + +PHP4 build fix + + + + + stable + stable + + + 3.0.2 + 3.0.0 + + 2005-07-05 + +Default to mmap and add a better error message for shmget failures + + + + + stable + stable + + + 3.0.3 + 3.0.0 + + 2005-07-07 + +Fix compile problem against PHP 5.0.x + + + + + stable + stable + + + 3.0.4 + 3.0.0 + + 2005-07-18 + +Add win32 support from Edin. +Add --with-apxs switch to work around problem when loading APC into Apache binary compiled with LFS switches +A couple of other minor fixes. + + + + + stable + stable + + + 3.0.5 + 3.0.0 + + 2005-07-27 + +Make it easier for sapis that only populate file_handle->filename to use APC. (Rasmus) +Support extensions such as bcompiler that need to hook into compile_file. (Val) +Ralf Becker's apcgui code has now become the default apc.php status page. (Ralf, Rasmus, Ilia) +Segfault in cache cleanup code (Ilia, Rasmus) + + + + + stable + stable + + + 3.0.6 + 3.0.0 + + 2005-07-30 + +Added apc.php to package.xml file. +Track per-entry memory usage. (Val) +Various apc.php fixes and enhancements. (Ralf, Ilia, Rasmus) +fcntl locking robustness fixes. (Rasmus) +Shared read-locks where possible. (Rasmus) +Added file_update_protection configuration parameter. (Rasmus) +Windows ZTS fixes (Frank) + + + + + stable + stable + + + 3.0.7 + 3.0.0 + + 2005-08-16 + +Fix to apc.php to show final segment in frag chart. (Ilia) +A couple of win32 fixes. (Frank) +Add apc.enable_cli ini directive. (Rasmus) +Add test cases. (Marcus) +Fix apc_define_constants() bug - http://pecl.php.net/bugs/5084 (Rasmus) +Simplify user cache handling by removing the user_cache_stack (Rasmus) +Fix apc_fetch() memory corruption (Andrei,Rasmus) +Added apc.max_file_size INI setting that allows exclusion of large files from being cached. Default file size limit, 1 megabyte. (Ilia) + + + + + stable + stable + + + 3.0.8 + 3.0.0 + + 2005-08-24 + +Fix invalid free in globals destructor introduced in 3.0.7 (Rasmus) +Cache corruption fix in cache-full cleanup code (Gopal) + + + + + stable + stable + + + 3.0.9 + 3.0.0 + + 2006-03-04 + +Eliminate rand() call when slam_defense is not set (Rasmus) +Fix for __isset problem (Gopal) +Rewrite allocator from a "best fit" to a "next fit" algorithm (Rasmus) +Added a Cache Full counter so we have an idea how many times the segment has filled up causing an expunge (Rasmus) +Report back the correct number of available bytes in the segment instead of the allocated bytes. (Rasmus) +Add cache busy flag which is set when an expunge is underway (Rasmus) +Add automatic serialization of objects in apc_store() (Marcus) +64-bit .ini flag fix (Rasmus) +Static members fix (Gopal) +sma_cleanup() mem leak fix (Rasmus) +Fix for http://pecl.php.net/bugs/5311 (Rasmus) +Fix autoglobals JIT bug (Gopal) +Fix instance bug (Gopal) +Add a lock cleanup safety net to request shutdown (Rasmus) +Fix apc.slam_defense edge-case bug (Rasmus) +User entry memory usage tracking support (Ilia) +Allow keys used in apc_store/apc_fetch/apc_delete to be binary safe and prevent conflicts between keys that are found at the start of other keys. (Ilia) + + + + + stable + stable + + + 3.0.10 + 3.0.0 + + 2006-03-11 + +* Add apc.stat ini flag which defaults to 1. If set to 0, the main script and any fullpath + includes will not be stat'ed for any changes. You will have to restart the server if you + change anything. This mode increases performance quite a bit, especially if you have a + lot of includes. + +* Get rid of the lock safety net hack I added in 3.0.9. It seems to cause more problems + than it solves. I'll need to revisit locking and signal handling at some point soon. + + + + + stable + stable + + + 3.0.11 + 3.0.0 + + 2006-08-16 + +* Made --enable-apc-mmap the default compile option (for real this time) + +* Add an optional flag to apc_cache_info() and some apc.php tweaks to make it + only fetch header information to make it useful when you have tens of + thousands of entries. (Brian Shire) + +* 64-bit fixes (George) + +* Don't mix Full Path and Inode keys (George) + +* Override ZEND_INCLUDE_OR_EVAL opcode (when possible) to speed up use of + require_once() and include_once() statements. (Sara) + +* Add a non-blocking write_lock for cache inserts. This is a better approach + to prevent cache slams and deprecates the slam_defense setting. (Rasmus) + +* A bit of work on the optimizer. (Sara) + +* Various memory issues resolved. (Gopal) + + + + + stable + stable + + + 3.0.12 + 3.0.0 + + 2006-09-04 + +* Fix stray debug message + +* Work on the optimizer - still not stable (Gopal, Ilia, Sara) + +* Performance boost - Replace multiple loops over the opcode + array with a single loop for copying, jump fixups and auto + global pre-fetches. (Gopal) + +* Perform fetch_global checks only in php5 and only if + auto_globals_jit is enabled. (Gopal) + +* Fix bug #8579 - scrub php4 classes' function_table and default + properties before inserting into cache. (Gopal) + +* Fix bug #8606 - ZEND_FETCH_GLOBAL is not an opcode, but is a + op1->type. The opcodes applicable are ZEND_FETCH_R and + ZEND_FETCH_W. (Gopal) + +* PHP 5.2 Compatibility (Gopal) + +* Make the include_once override optional - default off (Sara) + +* Fixed crash when apc run in CLI, but enable_cli is off. (Ilia) + +* Ensure that the apc_globals->cache_stack is cleared before the + shm cache is destroyed. Fixes segfault for corner-case i.e request + shutdown (apc_deactivate) is not called before module shutdown + calls (php_apc_shutdown_globals) (Gopal) + +* TSRM fixes (ensure ts_free_id before apc.so is dlclosed) (Gopal) + +* Fix memory leak of apc_cache_info_t->deleted_list (Gopal) + + + + + stable + stable + + + 3.0.12p1 + 3.0.0 + + 2006-09-05 + +* The only change here is a trivial PHP 4 build fix. + + + + + stable + stable + + + 3.0.12p2 + 3.0.0 + + 2006-09-05 + +* Let's get the version number right. 3.0.12p2 now. + + + + + stable + stable + + + 3.0.13 + 3.0.0 + + 2007-02-24 + +* PHP 5.2 file upload progress tracking support (Rasmus) +* Pthread mutex and spin locks (Shire) +* Recursive zval support for apc_fetch/_store (Shire, Gopal) +* apc.stat_ctime flag for ctime checks (Rasmus) +* Multiple key fetches with apc_fetch (Shire) +* Canary checks for shm memory deallocation (Gopal) +* Add hooks for external optimizer (Shire) +* Obsolete and remove apc optimizer (Gopal) +* APC info changes - cache insert rate, hit and miss rates (Shire) +* Fix apc_load_constants (Gopal) +* Rewrite dump opcode code to use vld (Gopal) +* Use apc_[ewn]print functions for error reporting (Shire) +* Auto global fixes and refactoring (Gopal, Shire) +* Fix memory leaks in object serialization (Ilia) +* Memory cleanup code for destructor order (Gopal) +* Win32 build fixes (Ilia, Wez) +* ZTS and Php 4 build fixes (Bjori) +* Add apc_add() function (Rasmus) +* Add optional limited flag to apc_sma_info() (Rasmus) + + + + + stable + stable + + + 3.0.14 + 3.0.0 + + 2007-04-02 + +* Build fix (Shire) +* Don't hook the upload hook if APC is disabled (Rasmus) +* Local shadow cache support (Gopal) +* Avoid uneccessary loops over op_arrays for "known" auto-globals (Gopal) +* Fix apc_add() to overwrite timed out user entries (Rasmus) +* Fix double inclusion of files with conditional classes in php4 (Gopal) +* Allocator fixes to reduce fragmentation (Gopal) + + + + + stable + stable + + + 3.0.15 + 3.0.0 + + 2007-10-18 + +* Eliminate a per-request time() syscall (Rasmus) +* Added rfc1867 prefix, name, and freq ini options (Shire) +* Allow deletion of individual user cache entries via apc.php (Sara) +* Fix overzealous cleanup during RSHUTDOWN (Gopal) +* Fix memory alignment and locking issues (Gopal) +* Make apc_compile insert/replace entries (Shire) +* Make mixed inheritance recompile & cache afresh (Gopal) +* Make nostat mode search include_path for canonicalization (Gopal) +* ZTS & other compile fixes (Gopal, Edin, Shire) + + + + + 3.0.16 + 3.0.0 + + + stable + stable + + PHP License + 2008-03-26 + +* Fix for longstanding cache-full crash (Christian Seiler) + http://news.php.net/php.pecl.dev/4951 for the details +* Added optional shm unmap on a fatal signal feature (Lucas Nealan) +* Added PTHREAD_MUTEX_ADAPTIVE_NP option pthread locks (Paul Saab) +* Minor cleanups (Lucas Nealan) +* Added configure option to enable apc_cache_info('filehits') (Shire) + + + + + 3.0.17 + 3.0.0 + + + stable + stable + + PHP License + 2008-03-29 + +* Crash fixes +* Fix apc_add() cache expunge bug (Rasmus) +* Added parameter to apc_fetch to determine success/failure when fetching booleans (shire) +* Fix misc. memleaks (shire) + + + + + 3.0.18 + 3.0.0 + + + stable + stable + + PHP License + 2008-03-29 + +- Revert apc_expunge_cb bug-fix +- Misc memleaks + + + + + 3.0.19 + 3.0.0 + + + stable + stable + + PHP License + 2008-05-15 + +- Safe-mode and fast-cgi fixes +- Fix double-free of builtin_functions +- php 5.3 fixes + + + + + 3.1.1 + 3.1.0 + + + beta + beta + + PHP License + 2008-12-12 + +- PHP4 compatibilty break +- apc_pool allocator (Gopal) +- doubly-linked sma allocator (Shire) +- php 5.3 gc compatibility (Gopal) +- APCIterator for easy access (Shire) +- apc_delete_file (Shire) +- apc_inc/apc_dec/apc_cas functions (Shire) +- apc.canonicalize (Gopal) +- apc.preload_path (Gopal) +- apc.rfc1867_ttl (Shire) +- apc.file_md5 (Shire) +- consolidate locking macros (Shire) +- remove futex/TSRM locks (Shire) +- non-blocking semaphore locks (Shire) +- zval* object rework (Gopal) + + + + + 3.1.2 + 3.1.0 + + + beta + beta + + PHP License + 2008-12-12 + +- pecl package.xml/build fixes (bjori) + + + + + 3.1.3 + 3.1.0 + + + beta + beta + + PHP License + 2009-08-13 + +- pecl package.xml/build fixes (bjori) +- 5.3 support + test-cases (Gopal) +- Lazy loading support (Shire) +- Fix PCRE module init order issues (Shire) +- APCIterator fixes (Shire) +- Cache slam checks (Gopal) +- ZEND_JMP_SET support (Shire) +- apc.use_request_time option (shire) +- apc.php hostname fixes (Shire) +- memprotect framework (Gopal) +- Win32 build-fixes (Kalle) + + + + + 3.1.3p1 + 3.1.0 + + + beta + beta + + PHP License + 2009-08-14 + +- fix pecl build / package.xml (Gopal) + + + + + 3.1.4 + 3.1.0 + + + beta + beta + + PHP License + 2010-08-05 + +- Windows builds may now have filehits and memory protection if enabled (Kalle) +- Renamed the memory protection configure option to --enable-apc-memprotect (Kalle, Shire) +- ZTS fixes and optimizations (Kalle, Felipe) +- Win32 stat support (Pierre, Kalle) +- Added support for interned strings, run-time caches and Zend Engine 2.4 (Dmitry) +- Added apc_exists() (Rasmus) +- Fixed potential XSS in apc.php, CVE-2010-3294 (Pierre, Matt Chapman) +- Fixed pecl bug #17597 (keys with embedded NUL) (Gopal) +- Fixed pecl bug #17650 (Fix goto jump offsets) (Gopal) +- Fixed pecl bug #17527 (Standardized error reporting) (Gopal, Paul Dragoonis) +- Fixed pecl bug #17089 (Scrub the constant table of all inherited members before caching) (Gopal) +- Fixed pecl bug #16860 (files can be included more than once even when include/require_once are used) (Pierre) +- Fixed pecl bug #16717 (apc_fetch dies after 1 hour, regardless of ttl settings) (Kalle) +- Fixed pecl bug #17597 (apc user cache keys with embedded NULs) (Gopal) +- Fixed pecl bug #13583 (apc upload progress fixes) (Gopal) + + + + + 3.1.5 + 3.1.0 + + + beta + stable + + PHP License + 2010-11-01 + +- Reduce usage of CG(open_files) (mkoppanen at php dot net) +- Add support for php-trunk, new op code, new internals string format, etc. (Dmitry) +- apc_debug are not compiler-valid NOPs for non-debug builds + +- Fixed relative paths resolution when ./foo/a.php or ../foo/a.php (or similar path) + are used. 'foo/a.php' path behaviors remain unchanged +- Fixed a possible memory corruption, when partial path cannot be resolved + by expand_filepath() +- Fixed notices in apc.php (Tomasz Krawczyk) + +- Fixed Bug #17978: standardize user keys to include NULs in identifier_len. Z_STRLEN_P() doesn't, so add to it. +- Fixed bug #16966, apc produces tons of warnings "Unable to allocate memory for pool". + +- Added --enable-apc-debug configuration argument to enable debugging (Kalle) +- Added support for internal debugging on Windows (Kalle) +- ZTS optimizations (Kalle) + + + + --- /dev/null +++ b/ext/apc/pgsql_s_lock.c @@ -0,0 +1,391 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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. | + +----------------------------------------------------------------------+ + | The following code was ported from the PostgreSQL project, please | + | see appropriate copyright notices that follow. | + | Initial conversion by Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: pgsql_s_lock.c 302175 2010-08-13 06:20:28Z kalle $ */ + +/*------------------------------------------------------------------------- + * + * s_lock.c + * Hardware-dependent implementation of spinlocks. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/storage/lmgr/s_lock.c,v 1.47 2006/10/04 00:29:58 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +/* #include "postgres.h" -- Removed for APC */ + +/* -- Added for APC -- */ +#include "apc.h" +#ifdef APC_SPIN_LOCKS + +#ifdef S_LOCK_TEST +#include +#endif +#ifndef WIN32 +#include +#endif +/* ---- */ + +#include +#ifdef WIN32 +#include "win32/unistd.h" +#else +#include +#endif + +/* #include "storage/s_lock.h" -- Removed for APC */ +#include "pgsql_s_lock.h" + +static int spins_per_delay = DEFAULT_SPINS_PER_DELAY; + + +/* -- APC specific additions ------------------------------*/ +/* The following dependencies have been copied from + * other pgsql source files. The original locations + * have been noted. + */ + +/* -- from include/c.h -- */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* -- from include/pg_config_manual.h -- */ +#define MAX_RANDOM_VALUE (0x7FFFFFFF) + +/* + * Max + * Return the maximum of two numbers. + */ +#define Max(x, y) ((x) > (y) ? (x) : (y)) + +/* -- from include/c.h -- */ +/* + * Min + * Return the minimum of two numbers. + */ +#define Min(x, y) ((x) < (y) ? (x) : (y)) + + +/* -- from backend/port/win32/signal.c -- */ +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: although the delay is specified in microseconds, the effective + * resolution is only 1/HZ, or 10 milliseconds, on most Unixen. Expect + * the requested delay to be rounded up to the next resolution boundary. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timeval delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_usec = microsec % 1000000L; + (void) select(0, NULL, NULL, NULL, &delay); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +/* -- End APC specific additions ------------------------------*/ + + +/* + * s_lock_stuck() - complain about a stuck spinlock + */ +static void +s_lock_stuck(volatile slock_t *lock, const char *file, int line TSRMLS_DC) +{ +#if defined(S_LOCK_TEST) + fprintf(stderr, + "\nStuck spinlock (%p) detected at %s:%d.\n", + lock, file, line); + exit(1); +#else + /* -- Removed for APC + elog(PANIC, "stuck spinlock (%p) detected at %s:%d", + lock, file, line); + */ + apc_error("Stuck spinlock (%p) detected" TSRMLS_CC, lock); +#endif +} + + +/* + * s_lock(lock) - platform-independent portion of waiting for a spinlock. + */ +void +s_lock(volatile slock_t *lock, const char *file, int line TSRMLS_DC) +{ + /* + * We loop tightly for awhile, then delay using pg_usleep() and try again. + * Preferably, "awhile" should be a small multiple of the maximum time we + * expect a spinlock to be held. 100 iterations seems about right as an + * initial guess. However, on a uniprocessor the loop is a waste of + * cycles, while in a multi-CPU scenario it's usually better to spin a bit + * longer than to call the kernel, so we try to adapt the spin loop count + * depending on whether we seem to be in a uniprocessor or multiprocessor. + * + * Note: you might think MIN_SPINS_PER_DELAY should be just 1, but you'd + * be wrong; there are platforms where that can result in a "stuck + * spinlock" failure. This has been seen particularly on Alphas; it seems + * that the first TAS after returning from kernel space will always fail + * on that hardware. + * + * Once we do decide to block, we use randomly increasing pg_usleep() + * delays. The first delay is 1 msec, then the delay randomly increases to + * about one second, after which we reset to 1 msec and start again. The + * idea here is that in the presence of heavy contention we need to + * increase the delay, else the spinlock holder may never get to run and + * release the lock. (Consider situation where spinlock holder has been + * nice'd down in priority by the scheduler --- it will not get scheduled + * until all would-be acquirers are sleeping, so if we always use a 1-msec + * sleep, there is a real possibility of starvation.) But we can't just + * clamp the delay to an upper bound, else it would take a long time to + * make a reasonable number of tries. + * + * We time out and declare error after NUM_DELAYS delays (thus, exactly + * that many tries). With the given settings, this will usually take 2 or + * so minutes. It seems better to fix the total number of tries (and thus + * the probability of unintended failure) than to fix the total time + * spent. + * + * The pg_usleep() delays are measured in milliseconds because 1 msec is a + * common resolution limit at the OS level for newer platforms. On older + * platforms the resolution limit is usually 10 msec, in which case the + * total delay before timeout will be a bit more. + */ +#define MIN_SPINS_PER_DELAY 10 +#define MAX_SPINS_PER_DELAY 1000 +#define NUM_DELAYS 1000 +#define MIN_DELAY_MSEC 1 +#define MAX_DELAY_MSEC 1000 + + int spins = 0; + int delays = 0; + int cur_delay = 0; + + while (TAS(lock)) + { + /* CPU-specific delay each time through the loop */ + SPIN_DELAY(); + + /* Block the process every spins_per_delay tries */ + if (++spins >= spins_per_delay) + { + if (++delays > NUM_DELAYS) + s_lock_stuck(lock, file, line TSRMLS_CC); + + if (cur_delay == 0) /* first time to delay? */ + cur_delay = MIN_DELAY_MSEC; + + pg_usleep(cur_delay * 1000L); + +#if defined(S_LOCK_TEST) + fprintf(stdout, "*"); + fflush(stdout); +#endif + + /* increase delay by a random fraction between 1X and 2X */ + cur_delay += (int) (cur_delay * + ((double) rand() / (double) MAX_RANDOM_VALUE) + 0.5); + /* wrap back to minimum delay when max is exceeded */ + if (cur_delay > MAX_DELAY_MSEC) + cur_delay = MIN_DELAY_MSEC; + + spins = 0; + } + } + + /* + * If we were able to acquire the lock without delaying, it's a good + * indication we are in a multiprocessor. If we had to delay, it's a sign + * (but not a sure thing) that we are in a uniprocessor. Hence, we + * decrement spins_per_delay slowly when we had to delay, and increase it + * rapidly when we didn't. It's expected that spins_per_delay will + * converge to the minimum value on a uniprocessor and to the maximum + * value on a multiprocessor. + * + * Note: spins_per_delay is local within our current process. We want to + * average these observations across multiple backends, since it's + * relatively rare for this function to even get entered, and so a single + * backend might not live long enough to converge on a good value. That + * is handled by the two routines below. + */ + if (cur_delay == 0) + { + /* we never had to delay */ + if (spins_per_delay < MAX_SPINS_PER_DELAY) + spins_per_delay = Min(spins_per_delay + 100, MAX_SPINS_PER_DELAY); + } + else + { + if (spins_per_delay > MIN_SPINS_PER_DELAY) + spins_per_delay = Max(spins_per_delay - 1, MIN_SPINS_PER_DELAY); + } +} + + +#if 0 /* -- APC doesn't use the set_spins_per_delay or update_spins_per_delay -- */ +/* + * Set local copy of spins_per_delay during backend startup. + * + * NB: this has to be pretty fast as it is called while holding a spinlock + */ +void +set_spins_per_delay(int shared_spins_per_delay) +{ + spins_per_delay = shared_spins_per_delay; +} + +/* + * Update shared estimate of spins_per_delay during backend exit. + * + * NB: this has to be pretty fast as it is called while holding a spinlock + */ +int +update_spins_per_delay(int shared_spins_per_delay) +{ + /* + * We use an exponential moving average with a relatively slow adaption + * rate, so that noise in any one backend's result won't affect the shared + * value too much. As long as both inputs are within the allowed range, + * the result must be too, so we need not worry about clamping the result. + * + * We deliberately truncate rather than rounding; this is so that single + * adjustments inside a backend can affect the shared estimate (see the + * asymmetric adjustment rules above). + */ + return (shared_spins_per_delay * 15 + spins_per_delay) / 16; +} +#endif + +/* + * Various TAS implementations that cannot live in s_lock.h as no inline + * definition exists (yet). + * In the future, get rid of tas.[cso] and fold it into this file. + * + * If you change something here, you will likely need to modify s_lock.h too, + * because the definitions for these are split between this file and s_lock.h. + */ + + +#ifdef HAVE_SPINLOCKS /* skip spinlocks if requested */ + + +#if defined(__GNUC__) + +/* + * All the gcc flavors that are not inlined + */ + + +/* + * Note: all the if-tests here probably ought to be testing gcc version + * rather than platform, but I don't have adequate info to know what to + * write. Ideally we'd flush all this in favor of the inline version. + */ +#if defined(__m68k__) && !defined(__linux__) +/* really means: extern int tas(slock_t* **lock); */ +static void +tas_dummy() +{ + __asm__ __volatile__( +#if defined(__NetBSD__) && defined(__ELF__) +/* no underscore for label and % for registers */ + "\ +.global tas \n\ +tas: \n\ + movel %sp@(0x4),%a0 \n\ + tas %a0@ \n\ + beq _success \n\ + moveq #-128,%d0 \n\ + rts \n\ +_success: \n\ + moveq #0,%d0 \n\ + rts \n" +#else + "\ +.global _tas \n\ +_tas: \n\ + movel sp@(0x4),a0 \n\ + tas a0@ \n\ + beq _success \n\ + moveq #-128,d0 \n\ + rts \n\ +_success: \n\ + moveq #0,d0 \n\ + rts \n" +#endif /* __NetBSD__ && __ELF__ */ + ); +} +#endif /* __m68k__ && !__linux__ */ +#else /* not __GNUC__ */ + +/* + * All non gcc + */ + + +#if defined(sun3) +static void +tas_dummy() /* really means: extern int tas(slock_t + * *lock); */ +{ + asm("LLA0:"); + asm(" .data"); + asm(" .text"); + asm("|#PROC# 04"); + asm(" .globl _tas"); + asm("_tas:"); + asm("|#PROLOGUE# 1"); + asm(" movel sp@(0x4),a0"); + asm(" tas a0@"); + asm(" beq LLA1"); + asm(" moveq #-128,d0"); + asm(" rts"); + asm("LLA1:"); + asm(" moveq #0,d0"); + asm(" rts"); + asm(" .data"); +} +#endif /* sun3 */ +#endif /* not __GNUC__ */ +#endif /* HAVE_SPINLOCKS */ + +#endif /* APC_SPIN_LOCKS */ --- /dev/null +++ b/ext/apc/pgsql_s_lock.h @@ -0,0 +1,928 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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. | + +----------------------------------------------------------------------+ + | The following code was ported from the PostgreSQL project, please | + | see appropriate copyright notices that follow. | + | Initial conversion by Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: pgsql_s_lock.h 302175 2010-08-13 06:20:28Z kalle $ */ + +/*------------------------------------------------------------------------- + * + * s_lock.h + * Hardware-dependent implementation of spinlocks. + * + * NOTE: none of the macros in this file are intended to be called directly. + * Call them through the hardware-independent macros in spin.h. + * + * The following hardware-dependent macros must be provided for each + * supported platform: + * + * void S_INIT_LOCK(slock_t *lock) + * Initialize a spinlock (to the unlocked state). + * + * void S_LOCK(slock_t *lock) + * Acquire a spinlock, waiting if necessary. + * Time out and abort() if unable to acquire the lock in a + * "reasonable" amount of time --- typically ~ 1 minute. + * + * void S_UNLOCK(slock_t *lock) + * Unlock a previously acquired lock. + * + * bool S_LOCK_FREE(slock_t *lock) + * Tests if the lock is free. Returns TRUE if free, FALSE if locked. + * This does *not* change the state of the lock. + * + * void SPIN_DELAY(void) + * Delay operation to occur inside spinlock wait loop. + * + * Note to implementors: there are default implementations for all these + * macros at the bottom of the file. Check if your platform can use + * these or needs to override them. + * + * Usually, S_LOCK() is implemented in terms of an even lower-level macro + * TAS(): + * + * int TAS(slock_t *lock) + * Atomic test-and-set instruction. Attempt to acquire the lock, + * but do *not* wait. Returns 0 if successful, nonzero if unable + * to acquire the lock. + * + * TAS() is NOT part of the API, and should never be called directly. + * + * CAUTION: on some platforms TAS() may sometimes report failure to acquire + * a lock even when the lock is not locked. For example, on Alpha TAS() + * will "fail" if interrupted. Therefore TAS() should always be invoked + * in a retry loop, even if you are certain the lock is free. + * + * ANOTHER CAUTION: be sure that TAS() and S_UNLOCK() represent sequence + * points, ie, loads and stores of other values must not be moved across + * a lock or unlock. In most cases it suffices to make the operation be + * done through a "volatile" pointer. + * + * On most supported platforms, TAS() uses a tas() function written + * in assembly language to execute a hardware atomic-test-and-set + * instruction. Equivalent OS-supplied mutex routines could be used too. + * + * If no system-specific TAS() is available (ie, HAVE_SPINLOCKS is not + * defined), then we fall back on an emulation that uses SysV semaphores + * (see spin.c). This emulation will be MUCH MUCH slower than a proper TAS() + * implementation, because of the cost of a kernel call per lock or unlock. + * An old report is that Postgres spends around 40% of its time in semop(2) + * when using the SysV semaphore code. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/include/storage/s_lock.h,v 1.157 2006/06/07 22:24:45 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef S_LOCK_H +#define S_LOCK_H + +/** APC namespace protection ************************************************/ +/* hack to protect against any possible runtime namespace collisions...*/ +#define pg_usleep apc_spin_pg_usleep +#define s_lock apc_spin_s_lock +#define spins_per_delay apc_spin_spins_per_delay +/****************************************************************************/ + + +/* #include "storage/pg_sema.h" -- Removed for APC */ + +#define HAVE_SPINLOCKS 1 /* -- Added for APC */ + +#ifdef HAVE_SPINLOCKS /* skip spinlocks if requested */ + + +#if defined(__GNUC__) || defined(__ICC) +/************************************************************************* + * All the gcc inlines + * Gcc consistently defines the CPU as __cpu__. + * Other compilers use __cpu or __cpu__ so we test for both in those cases. + */ + +/*---------- + * Standard gcc asm format (assuming "volatile slock_t *lock"): + + __asm__ __volatile__( + " instruction \n" + " instruction \n" + " instruction \n" +: "=r"(_res), "+m"(*lock) // return register, in/out lock value +: "r"(lock) // lock pointer, in input register +: "memory", "cc"); // show clobbered registers here + + * The output-operands list (after first colon) should always include + * "+m"(*lock), whether or not the asm code actually refers to this + * operand directly. This ensures that gcc believes the value in the + * lock variable is used and set by the asm code. Also, the clobbers + * list (after third colon) should always include "memory"; this prevents + * gcc from thinking it can cache the values of shared-memory fields + * across the asm code. Add "cc" if your asm code changes the condition + * code register, and also list any temp registers the code uses. + *---------- + */ + + +#ifdef __i386__ /* 32-bit i386 */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + /* + * Use a non-locking test before asserting the bus lock. Note that the + * extra test appears to be a small loss on some x86 platforms and a small + * win on others; it's by no means clear that we should keep it. + */ + __asm__ __volatile__( + " cmpb $0,%1 \n" + " jne 1f \n" + " lock \n" + " xchgb %0,%1 \n" + "1: \n" +: "+q"(_res), "+m"(*lock) +: +: "memory", "cc"); + return (int) _res; +} + +#define SPIN_DELAY() spin_delay() + +static __inline__ void +spin_delay(void) +{ + /* + * This sequence is equivalent to the PAUSE instruction ("rep" is + * ignored by old IA32 processors if the following instruction is + * not a string operation); the IA-32 Architecture Software + * Developer's Manual, Vol. 3, Section 7.7.2 describes why using + * PAUSE in the inner loop of a spin lock is necessary for good + * performance: + * + * The PAUSE instruction improves the performance of IA-32 + * processors supporting Hyper-Threading Technology when + * executing spin-wait loops and other routines where one + * thread is accessing a shared lock or semaphore in a tight + * polling loop. When executing a spin-wait loop, the + * processor can suffer a severe performance penalty when + * exiting the loop because it detects a possible memory order + * violation and flushes the core processor's pipeline. The + * PAUSE instruction provides a hint to the processor that the + * code sequence is a spin-wait loop. The processor uses this + * hint to avoid the memory order violation and prevent the + * pipeline flush. In addition, the PAUSE instruction + * de-pipelines the spin-wait loop to prevent it from + * consuming execution resources excessively. + */ + __asm__ __volatile__( + " rep; nop \n"); +} + +#endif /* __i386__ */ + + +#ifdef __x86_64__ /* AMD Opteron, Intel EM64T */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + /* + * On Opteron, using a non-locking test before the locking instruction + * is a huge loss. On EM64T, it appears to be a wash or small loss, + * so we needn't bother to try to distinguish the sub-architectures. + */ + __asm__ __volatile__( + " lock \n" + " xchgb %0,%1 \n" +: "+q"(_res), "+m"(*lock) +: +: "memory", "cc"); + return (int) _res; +} + +#define SPIN_DELAY() spin_delay() + +static __inline__ void +spin_delay(void) +{ + /* + * Adding a PAUSE in the spin delay loop is demonstrably a no-op on + * Opteron, but it may be of some use on EM64T, so we keep it. + */ + __asm__ __volatile__( + " rep; nop \n"); +} + +#endif /* __x86_64__ */ + + +#if defined(__ia64__) || defined(__ia64) /* Intel Itanium */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +#ifndef __INTEL_COMPILER + +static __inline__ int +tas(volatile slock_t *lock) +{ + long int ret; + + __asm__ __volatile__( + " xchg4 %0=%1,%2 \n" +: "=r"(ret), "+m"(*lock) +: "r"(1) +: "memory"); + return (int) ret; +} + +#else /* __INTEL_COMPILER */ + +static __inline__ int +tas(volatile slock_t *lock) +{ + int ret; + + ret = _InterlockedExchange(lock,1); /* this is a xchg asm macro */ + + return ret; +} + +#endif /* __INTEL_COMPILER */ +#endif /* __ia64__ || __ia64 */ + + +#if defined(__arm__) || defined(__arm) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + __asm__ __volatile__( + " swpb %0, %0, [%2] \n" +: "+r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return (int) _res; +} + +#endif /* __arm__ */ + + +/* S/390 and S/390x Linux (32- and 64-bit zSeries) */ +#if defined(__s390__) || defined(__s390x__) +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + int _res = 0; + + __asm__ __volatile__( + " cs %0,%3,0(%2) \n" +: "+d"(_res), "+m"(*lock) +: "a"(lock), "d"(1) +: "memory", "cc"); + return _res; +} + +#endif /* __s390__ || __s390x__ */ + + +#if defined(__sparc__) /* Sparc */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res; + + /* + * See comment in /pg/backend/port/tas/solaris_sparc.s for why this + * uses "ldstub", and that file uses "cas". gcc currently generates + * sparcv7-targeted binaries, so "cas" use isn't possible. + */ + __asm__ __volatile__( + " ldstub [%2], %0 \n" +: "=r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return (int) _res; +} + +#endif /* __sparc__ */ + + +/* PowerPC */ +#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) +#define HAS_TEST_AND_SET + +#if defined(__ppc64__) || defined(__powerpc64__) +typedef unsigned long slock_t; +#else +typedef unsigned int slock_t; +#endif + +#define TAS(lock) tas(lock) +/* + * NOTE: per the Enhanced PowerPC Architecture manual, v1.0 dated 7-May-2002, + * an isync is a sufficient synchronization barrier after a lwarx/stwcx loop. + */ +static __inline__ int +tas(volatile slock_t *lock) +{ + slock_t _t; + int _res; + + __asm__ __volatile__( +" lwarx %0,0,%3 \n" +" cmpwi %0,0 \n" +" bne 1f \n" +" addi %0,%0,1 \n" +" stwcx. %0,0,%3 \n" +" beq 2f \n" +"1: li %1,1 \n" +" b 3f \n" +"2: \n" +" isync \n" +" li %1,0 \n" +"3: \n" + +: "=&r"(_t), "=r"(_res), "+m"(*lock) +: "r"(lock) +: "memory", "cc"); + return _res; +} + +/* PowerPC S_UNLOCK is almost standard but requires a "sync" instruction */ +#define S_UNLOCK(lock) \ +do \ +{ \ + __asm__ __volatile__ (" sync \n"); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* powerpc */ + + +/* Linux Motorola 68k */ +#if (defined(__mc68000__) || defined(__m68k__)) && defined(__linux__) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int rv; + + __asm__ __volatile__( + " clrl %0 \n" + " tas %1 \n" + " sne %0 \n" +: "=d"(rv), "+m"(*lock) +: +: "memory", "cc"); + return rv; +} + +#endif /* (__mc68000__ || __m68k__) && __linux__ */ + + +/* + * VAXen -- even multiprocessor ones + * (thanks to Tom Ivar Helbekkmo) + */ +#if defined(__vax__) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int _res; + + __asm__ __volatile__( + " movl $1, %0 \n" + " bbssi $0, (%2), 1f \n" + " clrl %0 \n" + "1: \n" +: "=&r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return _res; +} + +#endif /* __vax__ */ + + +#if defined(__ns32k__) /* National Semiconductor 32K */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int _res; + + __asm__ __volatile__( + " sbitb 0, %1 \n" + " sfsd %0 \n" +: "=r"(_res), "+m"(*lock) +: +: "memory"); + return _res; +} + +#endif /* __ns32k__ */ + + +#if defined(__alpha) || defined(__alpha__) /* Alpha */ +/* + * Correct multi-processor locking methods are explained in section 5.5.3 + * of the Alpha AXP Architecture Handbook, which at this writing can be + * found at ftp://ftp.netbsd.org/pub/NetBSD/misc/dec-docs/index.html. + * For gcc we implement the handbook's code directly with inline assembler. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res; + + __asm__ __volatile__( + " ldq $0, %1 \n" + " bne $0, 2f \n" + " ldq_l %0, %1 \n" + " bne %0, 2f \n" + " mov 1, $0 \n" + " stq_c $0, %1 \n" + " beq $0, 2f \n" + " mb \n" + " br 3f \n" + "2: mov 1, %0 \n" + "3: \n" +: "=&r"(_res), "+m"(*lock) +: +: "memory", "0"); + return (int) _res; +} + +#define S_UNLOCK(lock) \ +do \ +{\ + __asm__ __volatile__ (" mb \n"); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* __alpha || __alpha__ */ + + +#if defined(__mips__) && !defined(__sgi) /* non-SGI MIPS */ +/* Note: on SGI we use the OS' mutex ABI, see below */ +/* Note: R10000 processors require a separate SYNC */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register volatile slock_t *_l = lock; + register int _res; + register int _tmp; + + __asm__ __volatile__( + " .set push \n" + " .set mips2 \n" + " .set noreorder \n" + " .set nomacro \n" + " ll %0, %2 \n" + " or %1, %0, 1 \n" + " sc %1, %2 \n" + " xori %1, 1 \n" + " or %0, %0, %1 \n" + " sync \n" + " .set pop " +: "=&r" (_res), "=&r" (_tmp), "+R" (*_l) +: +: "memory"); + return _res; +} + +/* MIPS S_UNLOCK is almost standard but requires a "sync" instruction */ +#define S_UNLOCK(lock) \ +do \ +{ \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set mips2 \n" \ + " .set noreorder \n" \ + " .set nomacro \n" \ + " sync \n" \ + " .set pop "); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* __mips__ && !__sgi */ + + +/* These live in s_lock.c, but only for gcc */ + + +#if defined(__m68k__) && !defined(__linux__) /* non-Linux Motorola 68k */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; +#endif + + +#endif /* __GNUC__ */ + + + +/* + * --------------------------------------------------------------------- + * Platforms that use non-gcc inline assembly: + * --------------------------------------------------------------------- + */ + +#if !defined(HAS_TEST_AND_SET) /* We didn't trigger above, let's try here */ + + +#if defined(USE_UNIVEL_CC) /* Unixware compiler */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +asm int +tas(volatile slock_t *s_lock) +{ +/* UNIVEL wants %mem in column 1, so we don't pg_indent this file */ +%mem s_lock + pushl %ebx + movl s_lock, %ebx + movl $255, %eax + lock + xchgb %al, (%ebx) + popl %ebx +} + +#endif /* defined(USE_UNIVEL_CC) */ + + +#if defined(__alpha) || defined(__alpha__) /* Tru64 Unix Alpha compiler */ +/* + * The Tru64 compiler doesn't support gcc-style inline asm, but it does + * have some builtin functions that accomplish much the same results. + * For simplicity, slock_t is defined as long (ie, quadword) on Alpha + * regardless of the compiler in use. LOCK_LONG and UNLOCK_LONG only + * operate on an int (ie, longword), but that's OK as long as we define + * S_INIT_LOCK to zero out the whole quadword. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#include +#define S_INIT_LOCK(lock) (*(lock) = 0) +#define TAS(lock) (__LOCK_LONG_RETRY((lock), 1) == 0) +#define S_UNLOCK(lock) __UNLOCK_LONG(lock) + +#endif /* __alpha || __alpha__ */ + + +#if defined(__hppa) || defined(__hppa__) /* HP PA-RISC, GCC and HP compilers */ +/* + * HP's PA-RISC + * + * See src/backend/port/hpux/tas.c.template for details about LDCWX. Because + * LDCWX requires a 16-byte-aligned address, we declare slock_t as a 16-byte + * struct. The active word in the struct is whichever has the aligned address; + * the other three words just sit at -1. + * + * When using gcc, we can inline the required assembly code. + */ +#define HAS_TEST_AND_SET + +typedef struct +{ + int sema[4]; +} slock_t; + +#define TAS_ACTIVE_WORD(lock) ((volatile int *) (((long) (lock) + 15) & ~15)) + +#if defined(__GNUC__) + +static __inline__ int +tas(volatile slock_t *lock) +{ + volatile int *lockword = TAS_ACTIVE_WORD(lock); + register int lockval; + + __asm__ __volatile__( + " ldcwx 0(0,%2),%0 \n" +: "=r"(lockval), "+m"(*lockword) +: "r"(lockword) +: "memory"); + return (lockval == 0); +} + +#endif /* __GNUC__ */ + +#define S_UNLOCK(lock) (*TAS_ACTIVE_WORD(lock) = -1) + +#define S_INIT_LOCK(lock) \ + do { \ + volatile slock_t *lock_ = (lock); \ + lock_->sema[0] = -1; \ + lock_->sema[1] = -1; \ + lock_->sema[2] = -1; \ + lock_->sema[3] = -1; \ + } while (0) + +#define S_LOCK_FREE(lock) (*TAS_ACTIVE_WORD(lock) != 0) + +#endif /* __hppa || __hppa__ */ + + +#if defined(__hpux) && defined(__ia64) && !defined(__GNUC__) + +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#include +#define TAS(lock) _Asm_xchg(_SZ_W, lock, 1, _LDHINT_NONE) + +#endif /* HPUX on IA64, non gcc */ + + +#if defined(__sgi) /* SGI compiler */ +/* + * SGI IRIX 5 + * slock_t is defined as a unsigned long. We use the standard SGI + * mutex API. + * + * The following comment is left for historical reasons, but is probably + * not a good idea since the mutex ABI is supported. + * + * This stuff may be supplemented in the future with Masato Kataoka's MIPS-II + * assembly from his NECEWS SVR4 port, but we probably ought to retain this + * for the R3000 chips out there. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#include "mutex.h" +#define TAS(lock) (test_and_set(lock,1)) +#define S_UNLOCK(lock) (test_then_and(lock,0)) +#define S_INIT_LOCK(lock) (test_then_and(lock,0)) +#define S_LOCK_FREE(lock) (test_then_add(lock,0) == 0) +#endif /* __sgi */ + + +#if defined(sinix) /* Sinix */ +/* + * SINIX / Reliant UNIX + * slock_t is defined as a struct abilock_t, which has a single unsigned long + * member. (Basically same as SGI) + */ +#define HAS_TEST_AND_SET + +#include "abi_mutex.h" +typedef abilock_t slock_t; + +#define TAS(lock) (!acquire_lock(lock)) +#define S_UNLOCK(lock) release_lock(lock) +#define S_INIT_LOCK(lock) init_lock(lock) +#define S_LOCK_FREE(lock) (stat_lock(lock) == UNLOCKED) +#endif /* sinix */ + + +#if defined(_AIX) /* AIX */ +/* + * AIX (POWER) + */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) _check_lock(lock, 0, 1) +#define S_UNLOCK(lock) _clear_lock(lock, 0) +#endif /* _AIX */ + + +#if defined (nextstep) /* Nextstep */ +#define HAS_TEST_AND_SET + +typedef struct mutex slock_t; + +#define APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE 0 /* -- APC: non-blocking lock not available in this case -- */ + +#define S_LOCK(lock) mutex_lock(lock) +#define S_UNLOCK(lock) mutex_unlock(lock) +#define S_INIT_LOCK(lock) mutex_init(lock) +/* For Mach, we have to delve inside the entrails of `struct mutex'. Ick! */ +#define S_LOCK_FREE(alock) ((alock)->lock == 0) +#endif /* nextstep */ + + +/* These are in s_lock.c */ + + +#if defined(sun3) /* Sun3 */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; +#endif + + +#if defined(__sun) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc)) +#define HAS_TEST_AND_SET + +#if defined(__i386) || defined(__x86_64__) || defined(__sparcv9) || defined(__sparcv8plus) +typedef unsigned int slock_t; +#else +typedef unsigned char slock_t; +#endif + +extern slock_t pg_atomic_cas(volatile slock_t *lock, slock_t with, + slock_t cmp); + +#define TAS(a) (pg_atomic_cas((a), 1, 0) != 0) +#endif + + +#ifdef WIN32_ONLY_COMPILER +typedef LONG slock_t; + +#define HAS_TEST_AND_SET +#define TAS(lock) (InterlockedCompareExchange(lock, 1, 0)) + +#define SPIN_DELAY() spin_delay() + +static __forceinline void +spin_delay(void) +{ + /* See comment for gcc code. Same code, MASM syntax */ + __asm rep nop; +} + +#endif + + +#endif /* !defined(HAS_TEST_AND_SET) */ + + +/* Blow up if we didn't have any way to do spinlocks */ +#ifndef HAS_TEST_AND_SET +/* -- APC: We have better options in APC than this, that should be specified explicitly so just fail out and notify the user -- */ +#error Spin locking is not available on your platform, please select another locking method (see ./configure --help). +/* #error PostgreSQL does not have native spinlock support on this platform. To continue the compilation, rerun configure using --disable-spinlocks. However, performance will be poor. Please report this to pgsql-bugs@postgresql.org. */ +#endif + + +#else /* !HAVE_SPINLOCKS */ + + +/* + * Fake spinlock implementation using semaphores --- slow and prone + * to fall foul of kernel limits on number of semaphores, so don't use this + * unless you must! The subroutines appear in spin.c. + */ + +/* -- Removed for APC +typedef PGSemaphoreData slock_t; + +extern bool s_lock_free_sema(volatile slock_t *lock); +extern void s_unlock_sema(volatile slock_t *lock); +extern void s_init_lock_sema(volatile slock_t *lock); +extern int tas_sema(volatile slock_t *lock); + +#define S_LOCK_FREE(lock) s_lock_free_sema(lock) +#define S_UNLOCK(lock) s_unlock_sema(lock) +#define S_INIT_LOCK(lock) s_init_lock_sema(lock) +#define TAS(lock) tas_sema(lock) +*/ + +#endif /* HAVE_SPINLOCKS */ + + +/* + * Default Definitions - override these above as needed. + */ + +#define APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE 1 /* -- APC: Non-blocking lock available for this case -- */ + +#if !defined(S_LOCK) +#define S_LOCK(lock) \ + do { \ + if (TAS(lock)) \ + s_lock((lock), __FILE__, __LINE__ TSRMLS_CC); \ + } while (0) +#endif /* S_LOCK */ + +#if !defined(S_LOCK_FREE) +#define S_LOCK_FREE(lock) (*(lock) == 0) +#endif /* S_LOCK_FREE */ + +#if !defined(S_UNLOCK) +#define S_UNLOCK(lock) (*((volatile slock_t *) (lock)) = 0) +#endif /* S_UNLOCK */ + +#if !defined(S_INIT_LOCK) +#define S_INIT_LOCK(lock) S_UNLOCK(lock) +#endif /* S_INIT_LOCK */ + +#if !defined(SPIN_DELAY) +#define SPIN_DELAY() ((void) 0) +#endif /* SPIN_DELAY */ + +#if !defined(TAS) +extern int tas(volatile slock_t *lock); /* in port/.../tas.s, or + * s_lock.c */ + +#define TAS(lock) tas(lock) +#endif /* TAS */ + + +/* + * Platform-independent out-of-line support routines + */ +extern void s_lock(volatile slock_t *lock, const char *file, int line TSRMLS_DC); + +/* Support for dynamic adjustment of spins_per_delay */ +#define DEFAULT_SPINS_PER_DELAY 100 + +#if 0 /* -- Removed from APC use -- */ +extern void set_spins_per_delay(int shared_spins_per_delay); +extern int update_spins_per_delay(int shared_spins_per_delay); +#endif + +#endif /* S_LOCK_H */ --- /dev/null +++ b/ext/apc/php_apc.c @@ -0,0 +1,1812 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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 303642 2010-09-21 15:42:01Z kalle $ */ + +#include "apc_zend.h" +#include "apc_cache.h" +#include "apc_iterator.h" +#include "apc_main.h" +#include "apc_sma.h" +#include "apc_lock.h" +#include "apc_bin.h" +#include "php_globals.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/flock_compat.h" +#ifdef HAVE_SYS_FILE_H +#include +#endif +#include "SAPI.h" +#include "rfc1867.h" +#include "php_apc.h" +#include "ext/standard/md5.h" + +#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_delete_file); +PHP_FUNCTION(apc_compile_file); +PHP_FUNCTION(apc_define_constants); +PHP_FUNCTION(apc_load_constants); +PHP_FUNCTION(apc_add); +PHP_FUNCTION(apc_inc); +PHP_FUNCTION(apc_dec); +PHP_FUNCTION(apc_cas); +PHP_FUNCTION(apc_bin_dump); +PHP_FUNCTION(apc_bin_load); +PHP_FUNCTION(apc_bin_dumpfile); +PHP_FUNCTION(apc_bin_loadfile); +PHP_FUNCTION(apc_exists); +/* }}} */ + +/* {{{ 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; + +static void php_apc_init_globals(zend_apc_globals* apc_globals TSRMLS_DC) +{ + apc_globals->filters = NULL; + apc_globals->compiled_filters = NULL; + apc_globals->initialized = 0; + apc_globals->cache_stack = apc_stack_create(0 TSRMLS_CC); + apc_globals->cache_by_default = 1; + apc_globals->fpstat = 1; + apc_globals->canonicalize = 1; + apc_globals->stat_ctime = 0; + apc_globals->write_lock = 1; + apc_globals->slam_defense = 1; + apc_globals->report_autofilter = 0; + apc_globals->include_once = 0; + apc_globals->apc_optimize_function = NULL; +#ifdef MULTIPART_EVENT_FORMDATA + apc_globals->rfc1867 = 0; + memset(&(apc_globals->rfc1867_data), 0, sizeof(apc_rfc1867_data)); +#endif + memset(&apc_globals->copied_zvals, 0, sizeof(HashTable)); + apc_globals->force_file_update = 0; + apc_globals->coredump_unmap = 0; + apc_globals->preload_path = NULL; + apc_globals->use_request_time = 1; + apc_globals->lazy_class_table = NULL; + apc_globals->lazy_function_table = NULL; +} + +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] TSRMLS_CC); + } + apc_efree(apc_globals->filters TSRMLS_CC); + } + + /* the stack should be empty */ + assert(apc_stack_size(apc_globals->cache_stack) == 0); + + /* apc cleanup */ + apc_stack_destroy(apc_globals->cache_stack TSRMLS_CC); + + /* the rest of the globals are cleaned up in apc_module_shutdown() */ +} + +static long apc_atol(const char *str, int str_len) +{ +#if PHP_MAJOR_VERSION >= 6 || PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3 + return zend_atol(str, str_len); +#else + /* Re-implement zend_atol() for 5.2.x */ + long retval; + + if (!str_len) { + str_len = strlen(str); + } + + retval = strtol(str, NULL, 0); + + if (str_len > 0) { + switch (str[str_len - 1]) { + case 'g': + case 'G': + retval *= 1024; + /* break intentionally missing */ + case 'm': + case 'M': + retval *= 1024; + /* break intentionally missing */ + case 'k': + case 'K': + retval *= 1024; + break; + } + } + + return retval; +#endif +} + +/* }}} */ + +/* {{{ PHP_INI */ + +static PHP_INI_MH(OnUpdate_filters) /* {{{ */ +{ + APCG(filters) = apc_tokenize(new_value, ',' TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +static PHP_INI_MH(OnUpdateShmSegments) /* {{{ */ +{ +#if APC_MMAP + if(zend_atoi(new_value, new_value_length)!=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) = zend_atoi(new_value, new_value_length); +#endif + return SUCCESS; +} +/* }}} */ + +static PHP_INI_MH(OnUpdateShmSize) /* {{{ */ +{ + long s = apc_atol(new_value, new_value_length); + + if(s <= 0) { + return FAILURE; + } + + if(s < 1048576L) { + /* if it's less than 1Mb, they are probably using the old syntax */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "apc.shm_size now uses M/G suffixes, please update your ini files"); + s = s * 1048576L; + } + + APCG(shm_size) = s; + + 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_error("rfc1867_freq must be greater than or equal to zero." TSRMLS_CC); + return FAILURE; + } + if(new_value[new_value_length-1] == '%') { + if(tmp > 100) { + apc_error("rfc1867_freq cannot be over 100%%" TSRMLS_CC); + return FAILURE; + } + APCG(rfc1867_freq) = tmp / 100.0; + } else { + APCG(rfc1867_freq) = tmp; + } + return SUCCESS; +} +/* }}} */ +#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", "32M", PHP_INI_SYSTEM, OnUpdateShmSize, shm_size, zend_apc_globals, apc_globals) +#ifdef ZEND_ENGINE_2_4 +STD_PHP_INI_ENTRY("apc.shm_strings_buffer", "4M", PHP_INI_SYSTEM, OnUpdateLong, shm_strings_buffer, zend_apc_globals, apc_globals) +#endif +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, OnUpdateLong, num_files_hint, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.user_entries_hint", "4096", PHP_INI_SYSTEM, OnUpdateLong, user_entries_hint, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.gc_ttl", "3600", PHP_INI_SYSTEM, OnUpdateLong, gc_ttl, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.ttl", "0", PHP_INI_SYSTEM, OnUpdateLong, ttl, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.user_ttl", "0", PHP_INI_SYSTEM, OnUpdateLong, 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.file_update_protection", "2", PHP_INI_SYSTEM, OnUpdateLong,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, OnUpdateLong, 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.canonicalize", "1", PHP_INI_SYSTEM, OnUpdateBool, canonicalize, 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.slam_defense", "1", PHP_INI_SYSTEM, OnUpdateBool, slam_defense, 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) +STD_PHP_INI_ENTRY("apc.rfc1867_ttl", "3600", PHP_INI_SYSTEM, OnUpdateLong, rfc1867_ttl, zend_apc_globals, apc_globals) +#endif +STD_PHP_INI_BOOLEAN("apc.coredump_unmap", "0", PHP_INI_SYSTEM, OnUpdateBool, coredump_unmap, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.preload_path", (char*)NULL, PHP_INI_SYSTEM, OnUpdateString, preload_path, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.file_md5", "0", PHP_INI_SYSTEM, OnUpdateBool, file_md5, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.use_request_time", "1", PHP_INI_ALL, OnUpdateBool, use_request_time, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.lazy_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, lazy_functions, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.lazy_classes", "0", PHP_INI_SYSTEM, OnUpdateBool, lazy_classes, 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_header(2, "APC Support", APCG(enabled) ? "enabled" : "disabled"); + php_info_print_table_row(2, "Version", PHP_APC_VERSION); +#ifdef __DEBUG_APC__ + php_info_print_table_row(2, "APC Debugging", "Enabled"); +#else + php_info_print_table_row(2, "APC Debugging", "Disabled"); +#endif +#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 + php_info_print_table_row(2, "Locking type", APC_LOCK_TYPE); + php_info_print_table_row(2, "Revision", "$Revision: 303642 $"); + 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 + apc_iterator_init(module_number TSRMLS_CC); + } + + zend_register_long_constant("APC_BIN_VERIFY_MD5", sizeof("APC_BIN_VERIFY_MD5"), APC_BIN_VERIFY_MD5, (CONST_CS | CONST_PERSISTENT), module_number TSRMLS_CC); + zend_register_long_constant("APC_BIN_VERIFY_CRC32", sizeof("APC_BIN_VERIFY_CRC32"), APC_BIN_VERIFY_CRC32, (CONST_CS | CONST_PERSISTENT), module_number TSRMLS_CC); + } + + 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 +#if HAVE_SIGACTION + apc_shutdown_signals(TSRMLS_C); +#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(TSRMLS_C); +#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; + char md5str[33]; + + 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 TSRMLS_CC); + } 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 TSRMLS_CC); + } + } else { + info = apc_cache_info(apc_cache, limited TSRMLS_CC); + } + + 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_double(return_value, "num_hits", (double)info->num_hits); + add_assoc_double(return_value, "num_misses", (double)info->num_misses); + add_assoc_double(return_value, "num_inserts", (double)info->num_inserts); + add_assoc_double(return_value, "expunges", (double)info->expunges); + + add_assoc_long(return_value, "start_time", info->start_time); + add_assoc_double(return_value, "mem_size", (double)info->mem_size); + add_assoc_long(return_value, "num_entries", info->num_entries); +#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_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 TSRMLS_CC); + 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); +#ifdef PHP_WIN32 + { + char buf[20]; + sprintf(buf, "%I64d", p->data.file.device); + add_assoc_string(link, "device", buf, 1); + + sprintf(buf, "%I64d", p->data.file.inode); + add_assoc_string(link, "inode", buf, 1); + } +#else + add_assoc_long(link, "device", p->data.file.device); + add_assoc_long(link, "inode", p->data.file.inode); +#endif + + + add_assoc_string(link, "type", "file", 1); + if(APCG(file_md5)) { + make_digest(md5str, p->data.file.md5); + add_assoc_string(link, "md5", md5str, 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_double(link, "num_hits", (double)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); +#ifdef PHP_WIN32 + { + char buf[20]; + sprintf(buf, "%I64d", p->data.file.device); + add_assoc_string(link, "device", buf, 1); + + sprintf(buf, "%I64d", p->data.file.inode); + add_assoc_string(link, "inode", buf, 1); + } +#else + add_assoc_long(link, "device", p->data.file.device); + add_assoc_long(link, "inode", p->data.file.inode); +#endif + + add_assoc_string(link, "type", "file", 1); + if(APCG(file_md5)) { + make_digest(md5str, p->data.file.md5); + add_assoc_string(link, "md5", md5str, 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_double(link, "num_hits", (double)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 TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto void apc_clear_cache([string cache]) */ +PHP_FUNCTION(apc_clear_cache) +{ + char *cache_type; + int ct_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache_type, &ct_len) == FAILURE) { + return; + } + + if(ct_len) { + if(!strcasecmp(cache_type, "user")) { + apc_cache_clear(apc_user_cache TSRMLS_CC); + RETURN_TRUE; + } + } + apc_cache_clear(apc_cache TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ 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 TSRMLS_CC); + + 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_double(return_value, "seg_size", (double)info->seg_size); + add_assoc_double(return_value, "avail_mem", (double)apc_sma_get_avail_mem()); + + if(limited) { + apc_sma_free_info(info TSRMLS_CC); + 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 TSRMLS_CC); +} +/* }}} */ + +/* {{{ */ +int _apc_update(char *strkey, int strkey_len, apc_cache_updater_t updater, void* data TSRMLS_DC) +{ + if(!APCG(enabled)) { + return 0; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + APCG(current_cache) = apc_user_cache; + + if (!_apc_cache_user_update(apc_user_cache, strkey, strkey_len + 1, updater, data TSRMLS_CC)) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + return 0; + } + + APCG(current_cache) = NULL; + HANDLE_UNBLOCK_INTERRUPTIONS(); + + return 1; +} +/* }}} */ + +/* {{{ _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; + apc_context_t ctxt={0,}; + int ret = 1; + + t = apc_time(); + + if(!APCG(enabled)) return 0; + + HANDLE_BLOCK_INTERRUPTIONS(); + + APCG(current_cache) = apc_user_cache; + + ctxt.pool = apc_pool_create(APC_SMALL_POOL, apc_sma_malloc, apc_sma_free, apc_sma_protect, apc_sma_unprotect TSRMLS_CC); + if (!ctxt.pool) { + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + return 0; + } + ctxt.copy = APC_COPY_IN_USER; + ctxt.force_update = 0; + + if(!ctxt.pool) { + ret = 0; + goto nocache; + } + + if (!apc_cache_make_user_key(&key, strkey, strkey_len, t)) { + goto freepool; + } + + if (apc_cache_is_last_key(apc_user_cache, &key, 0, t TSRMLS_CC)) { + goto freepool; + } + + if (!(entry = apc_cache_make_user_entry(strkey, strkey_len, val, &ctxt, ttl TSRMLS_CC))) { + goto freepool; + } + + if (!apc_cache_user_insert(apc_user_cache, key, entry, &ctxt, t, exclusive TSRMLS_CC)) { +freepool: + apc_pool_destroy(ctxt.pool TSRMLS_CC); + ret = 0; + } + +nocache: + + APCG(current_cache) = NULL; + + HANDLE_UNBLOCK_INTERRUPTIONS(); + + return ret; +} +/* }}} */ + +/* {{{ apc_store_helper(INTERNAL_FUNCTION_PARAMETERS, const int exclusive) + */ +static void apc_store_helper(INTERNAL_FUNCTION_PARAMETERS, const int exclusive) +{ + zval *key = NULL; + zval *val = NULL; + long ttl = 0L; + HashTable *hash; + HashPosition hpos; + zval **hentry; + char *hkey=NULL; + uint hkey_len; + ulong hkey_idx; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|zl", &key, &val, &ttl) == FAILURE) { + return; + } + + if (!key) RETURN_FALSE; + + if (Z_TYPE_P(key) == IS_ARRAY) { + hash = Z_ARRVAL_P(key); + array_init(return_value); + zend_hash_internal_pointer_reset_ex(hash, &hpos); + while(zend_hash_get_current_data_ex(hash, (void**)&hentry, &hpos) == SUCCESS) { + zend_hash_get_current_key_ex(hash, &hkey, &hkey_len, &hkey_idx, 0, &hpos); + if (hkey) { + if(!_apc_store(hkey, hkey_len, *hentry, (unsigned int)ttl, exclusive TSRMLS_CC)) { + add_assoc_long_ex(return_value, hkey, hkey_len, -1); /* -1: insertion error */ + } + hkey = NULL; + } else { + add_index_long(return_value, hkey_idx, -1); /* -1: insertion error */ + } + zend_hash_move_forward_ex(hash, &hpos); + } + return; + } else if (Z_TYPE_P(key) == IS_STRING) { + if (!val) RETURN_FALSE; + if(_apc_store(Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, val, (unsigned int)ttl, exclusive TSRMLS_CC)) + RETURN_TRUE; + } else { + apc_warning("apc_store expects key parameter to be a string or an array of key/value pairs." TSRMLS_CC); + } + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto int apc_store(mixed key, mixed var [, long ttl ]) + */ +PHP_FUNCTION(apc_store) { + apc_store_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int apc_add(mixed key, mixed var [, long ttl ]) + */ +PHP_FUNCTION(apc_add) { + apc_store_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ inc_updater */ + +struct _inc_update_args { + long step; + long lval; +}; + +static int inc_updater(apc_cache_t* cache, apc_cache_entry_t* entry, void* data) { + + struct _inc_update_args *args = (struct _inc_update_args*) data; + + zval* val = entry->data.user.val; + + if(Z_TYPE_P(val) == IS_LONG) { + Z_LVAL_P(val) += args->step; + args->lval = Z_LVAL_P(val); + return 1; + } + + return 0; +} +/* }}} */ + +/* {{{ proto long apc_inc(string key [, long step [, bool& success]]) + */ +PHP_FUNCTION(apc_inc) { + char *strkey; + int strkey_len; + struct _inc_update_args args = {1L, -1}; + zval *success = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz", &strkey, &strkey_len, &(args.step), &success) == FAILURE) { + return; + } + + if(_apc_update(strkey, strkey_len, inc_updater, &args TSRMLS_CC)) { + if(success) ZVAL_TRUE(success); + RETURN_LONG(args.lval); + } + + if(success) ZVAL_FALSE(success); + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto long apc_dec(string key [, long step [, bool &success]]) + */ +PHP_FUNCTION(apc_dec) { + char *strkey; + int strkey_len; + struct _inc_update_args args = {1L, -1}; + zval *success = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz", &strkey, &strkey_len, &(args.step), &success) == FAILURE) { + return; + } + + args.step = args.step * -1; + + if(_apc_update(strkey, strkey_len, inc_updater, &args TSRMLS_CC)) { + if(success) ZVAL_TRUE(success); + RETURN_LONG(args.lval); + } + + if(success) ZVAL_FALSE(success); + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ cas_updater */ +static int cas_updater(apc_cache_t* cache, apc_cache_entry_t* entry, void* data) { + long* vals = ((long*)data); + long old = vals[0]; + long new = vals[1]; + zval* val = entry->data.user.val; + + if(Z_TYPE_P(val) == IS_LONG) { + if(Z_LVAL_P(val) == old) { + Z_LVAL_P(val) = new; + return 1; + } + } + + return 0; +} +/* }}} */ + +/* {{{ proto int apc_cas(string key, int old, int new) + */ +PHP_FUNCTION(apc_cas) { + char *strkey; + int strkey_len; + long vals[2]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &strkey, &strkey_len, &vals[0], &vals[1]) == FAILURE) { + return; + } + + if(_apc_update(strkey, strkey_len, cas_updater, &vals 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); +} + +/* {{{ proto mixed apc_fetch(mixed key[, bool &success]) + */ +PHP_FUNCTION(apc_fetch) { + zval *key; + zval *success = NULL; + HashTable *hash; + HashPosition hpos; + zval **hentry; + zval *result; + zval *result_entry; + char *strkey; + int strkey_len; + apc_cache_entry_t* entry; + time_t t; + apc_context_t ctxt = {0,}; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &key, &success) == FAILURE) { + return; + } + + t = apc_time(); + + if (success) { + ZVAL_BOOL(success, 0); + } + + ctxt.pool = apc_pool_create(APC_UNPOOL, apc_php_malloc, apc_php_free, NULL, NULL TSRMLS_CC); + if (!ctxt.pool) { + apc_warning("Unable to allocate memory for pool." TSRMLS_CC); + RETURN_FALSE; + } + ctxt.copy = APC_COPY_OUT_USER; + ctxt.force_update = 0; + + 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 TSRMLS_CC); + if(entry) { + /* deep-copy returned shm zval to emalloc'ed return_value */ + apc_cache_fetch_zval(return_value, entry->data.user.val, &ctxt TSRMLS_CC); + apc_cache_release(apc_user_cache, entry TSRMLS_CC); + } else { + goto freepool; + } + } 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_warning("apc_fetch() expects a string or array of strings." TSRMLS_CC); + goto freepool; + } + entry = apc_cache_user_find(apc_user_cache, Z_STRVAL_PP(hentry), (Z_STRLEN_PP(hentry) + 1), t TSRMLS_CC); + 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, &ctxt TSRMLS_CC); + apc_cache_release(apc_user_cache, entry TSRMLS_CC); + 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); + } + RETVAL_ZVAL(result, 0, 1); + } else { + apc_warning("apc_fetch() expects a string or array of strings." TSRMLS_CC); +freepool: + apc_pool_destroy(ctxt.pool TSRMLS_CC); + RETURN_FALSE; + } + + if (success) { + ZVAL_BOOL(success, 1); + } + + apc_pool_destroy(ctxt.pool TSRMLS_CC); + return; +} +/* }}} */ + +/* {{{ proto mixed apc_exists(mixed key) + */ +PHP_FUNCTION(apc_exists) { + zval *key; + HashTable *hash; + HashPosition hpos; + zval **hentry; + char *strkey; + int strkey_len; + apc_cache_entry_t* entry; + zval *result; + zval *result_entry; + time_t t; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) == FAILURE) { + return; + } + + t = apc_time(); + + 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_exists(apc_user_cache, strkey, strkey_len + 1, t TSRMLS_CC); + if(entry) { + RETURN_TRUE; + } + } 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_warning("apc_exists() expects a string or array of strings." TSRMLS_CC); + RETURN_FALSE; + } + + entry = apc_cache_user_exists(apc_user_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1, t TSRMLS_CC); + if(entry) { + MAKE_STD_ZVAL(result_entry); + ZVAL_BOOL(result_entry, 1); + 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_warning("apc_exists() expects a string or array of strings." TSRMLS_CC); + } + + RETURN_FALSE; +} +/* }}} */ + + +/* {{{ proto mixed apc_delete(mixed keys) + */ +PHP_FUNCTION(apc_delete) { + zval *keys; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &keys) == FAILURE) { + return; + } + + if (Z_TYPE_P(keys) == IS_STRING) { + if (!Z_STRLEN_P(keys)) RETURN_FALSE; + if(apc_cache_user_delete(apc_user_cache, Z_STRVAL_P(keys), (Z_STRLEN_P(keys) + 1) TSRMLS_CC)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } else if (Z_TYPE_P(keys) == IS_ARRAY) { + HashTable *hash = Z_ARRVAL_P(keys); + HashPosition hpos; + zval **hentry; + array_init(return_value); + 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_warning("apc_delete() expects a string, array of strings, or APCIterator instance." TSRMLS_CC); + add_next_index_zval(return_value, *hentry); + Z_ADDREF_PP(hentry); + } else if(apc_cache_user_delete(apc_user_cache, Z_STRVAL_PP(hentry), (Z_STRLEN_PP(hentry) + 1) TSRMLS_CC) != 1) { + add_next_index_zval(return_value, *hentry); + Z_ADDREF_PP(hentry); + } + zend_hash_move_forward_ex(hash, &hpos); + } + return; + } else if (Z_TYPE_P(keys) == IS_OBJECT) { + if (apc_iterator_delete(keys TSRMLS_CC)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } else { + apc_warning("apc_delete() expects a string, array of strings, or APCIterator instance." TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ proto mixed apc_delete_file(mixed keys) + * Deletes the given files from the opcode cache. + * Accepts a string, array of strings, or APCIterator object. + * Returns True/False, or for an Array an Array of failed files. + */ +PHP_FUNCTION(apc_delete_file) { + zval *keys; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &keys) == FAILURE) { + return; + } + + if (Z_TYPE_P(keys) == IS_STRING) { + if (!Z_STRLEN_P(keys)) RETURN_FALSE; + if(apc_cache_delete(apc_cache, Z_STRVAL_P(keys), Z_STRLEN_P(keys) + 1 TSRMLS_CC) != 1) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } + } else if (Z_TYPE_P(keys) == IS_ARRAY) { + HashTable *hash = Z_ARRVAL_P(keys); + HashPosition hpos; + zval **hentry; + array_init(return_value); + 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_warning("apc_delete_file() expects a string, array of strings, or APCIterator instance." TSRMLS_CC); + add_next_index_zval(return_value, *hentry); + Z_ADDREF_PP(hentry); + } else if(apc_cache_delete(apc_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1 TSRMLS_CC) != 1) { + add_next_index_zval(return_value, *hentry); + Z_ADDREF_PP(hentry); + } + zend_hash_move_forward_ex(hash, &hpos); + } + return; + } else if (Z_TYPE_P(keys) == IS_OBJECT) { + if (apc_iterator_delete(keys TSRMLS_CC)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } else { + apc_warning("apc_delete_file() expects a string, array of strings, or APCIterator instance." TSRMLS_CC); + } +} +/* }}} */ + +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; + c.module_number = PHP_USER_CONSTANT; + 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 + 1, 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; + + t = apc_time(); + + entry = apc_cache_user_find(apc_user_cache, strkey, (strkey_len + 1), t TSRMLS_CC); + + if(entry) { + _apc_define_constants(entry->data.user.val, case_sensitive TSRMLS_CC); + apc_cache_release(apc_user_cache, entry TSRMLS_CC); + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto mixed apc_compile_file(mixed filenames [, bool atomic]) + */ +PHP_FUNCTION(apc_compile_file) { + zval *file; + zend_file_handle file_handle; + zend_op_array *op_array; + char** filters = NULL; + zend_bool cache_by_default = 1; + HashTable cg_function_table, cg_class_table; + HashTable *cg_orig_function_table, *cg_orig_class_table, *eg_orig_function_table, *eg_orig_class_table; + apc_cache_entry_t** cache_entries; + apc_cache_key_t* keys; + zend_op_array **op_arrays; + time_t t; + zval **hentry; + HashPosition hpos; + int i=0, c=0; + int *rval=NULL; + int count=0; + zend_bool atomic=1; + apc_context_t ctxt = {0,}; + zend_execute_data *orig_current_execute_data; + int atomic_fail; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &file, &atomic) == FAILURE) { + return; + } + + if (Z_TYPE_P(file) != IS_ARRAY && Z_TYPE_P(file) != IS_STRING) { + apc_warning("apc_compile_file argument must be a string or an array of strings" TSRMLS_CC); + RETURN_FALSE; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + APCG(current_cache) = apc_cache; + + /* reset filters and cache_by_default */ + 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(s), loading it into the cache */ + if (Z_TYPE_P(file) == IS_STRING) { + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = Z_STRVAL_P(file); + file_handle.free_filename = 0; + file_handle.opened_path = NULL; + + orig_current_execute_data = EG(current_execute_data); + zend_try { + op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC); + } zend_catch { + EG(current_execute_data) = orig_current_execute_data; + EG(in_execution) = 1; + CG(unclean_shutdown) = 0; + apc_warning("Error compiling %s in apc_compile_file." TSRMLS_CC, file_handle.filename); + op_array = NULL; + } zend_end_try(); + if(op_array != NULL) { + /* Free up everything */ + destroy_op_array(op_array TSRMLS_CC); + efree(op_array); + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } + zend_destroy_file_handle(&file_handle TSRMLS_CC); + + } else { /* IS_ARRAY */ + + array_init(return_value); + + t = apc_time(); + + op_arrays = ecalloc(Z_ARRVAL_P(file)->nNumOfElements, sizeof(zend_op_array*)); + cache_entries = ecalloc(Z_ARRVAL_P(file)->nNumOfElements, sizeof(apc_cache_entry_t*)); + keys = ecalloc(Z_ARRVAL_P(file)->nNumOfElements, sizeof(apc_cache_key_t)); + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(file), &hpos); + while(zend_hash_get_current_data_ex(Z_ARRVAL_P(file), (void**)&hentry, &hpos) == SUCCESS) { + if (Z_TYPE_PP(hentry) != IS_STRING) { + apc_warning("apc_compile_file array values must be strings, aborting." TSRMLS_CC); + break; + } + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = Z_STRVAL_PP(hentry); + file_handle.free_filename = 0; + file_handle.opened_path = NULL; + + if (!apc_cache_make_file_key(&(keys[i]), file_handle.filename, PG(include_path), t TSRMLS_CC)) { + add_assoc_long(return_value, Z_STRVAL_PP(hentry), -1); /* -1: compilation error */ + apc_warning("Error compiling %s in apc_compile_file." TSRMLS_CC, file_handle.filename); + break; + } + + if (keys[i].type == APC_CACHE_KEY_FPFILE) { + keys[i].data.fpfile.fullpath = estrndup(keys[i].data.fpfile.fullpath, keys[i].data.fpfile.fullpath_len); + } else if (keys[i].type == APC_CACHE_KEY_USER) { + keys[i].data.user.identifier = estrndup(keys[i].data.user.identifier, keys[i].data.user.identifier_len); + } + + orig_current_execute_data = EG(current_execute_data); + zend_try { + if (apc_compile_cache_entry(keys[i], &file_handle, ZEND_INCLUDE, t, &op_arrays[i], &cache_entries[i] TSRMLS_CC) != SUCCESS) { + op_arrays[i] = NULL; + cache_entries[i] = NULL; + add_assoc_long(return_value, Z_STRVAL_PP(hentry), -2); /* -2: input or cache insertion error */ + apc_warning("Error compiling %s in apc_compile_file." TSRMLS_CC, file_handle.filename); + } + } zend_catch { + EG(current_execute_data) = orig_current_execute_data; + EG(in_execution) = 1; + CG(unclean_shutdown) = 0; + op_arrays[i] = NULL; + cache_entries[i] = NULL; + add_assoc_long(return_value, Z_STRVAL_PP(hentry), -1); /* -1: compilation error */ + apc_warning("Error compiling %s in apc_compile_file." TSRMLS_CC, file_handle.filename); + } zend_end_try(); + + zend_destroy_file_handle(&file_handle TSRMLS_CC); + if(op_arrays[i] != NULL) { + count++; + } + + /* clean out the function/class tables */ + zend_hash_clean(&cg_function_table); + zend_hash_clean(&cg_class_table); + + zend_hash_move_forward_ex(Z_ARRVAL_P(file), &hpos); + i++; + } + + /* atomically update the cache if no errors or not atomic */ + ctxt.copy = APC_COPY_IN_OPCODE; + ctxt.force_update = 1; + if (count == i || !atomic) { + rval = apc_cache_insert_mult(apc_cache, keys, cache_entries, &ctxt, t, i TSRMLS_CC); + atomic_fail = 0; + } else { + atomic_fail = 1; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(file), &hpos); + for(c=0; c < i; c++) { + zend_hash_get_current_data_ex(Z_ARRVAL_P(file), (void**)&hentry, &hpos); + if (rval && rval[c] != 1) { + add_assoc_long(return_value, Z_STRVAL_PP(hentry), -2); /* -2: input or cache insertion error */ + if (cache_entries[c]) { + apc_pool_destroy(cache_entries[c]->pool TSRMLS_CC); + } + } + if (op_arrays[c]) { + destroy_op_array(op_arrays[c] TSRMLS_CC); + efree(op_arrays[c]); + } + if (atomic_fail && cache_entries[c]) { + apc_pool_destroy(cache_entries[c]->pool TSRMLS_CC); + } + if (keys[c].type == APC_CACHE_KEY_FPFILE) { + efree((void*)keys[c].data.fpfile.fullpath); + } else if (keys[c].type == APC_CACHE_KEY_USER) { + efree((void*)keys[c].data.user.identifier); + } + zend_hash_move_forward_ex(Z_ARRVAL_P(file), &hpos); + } + efree(op_arrays); + efree(keys); + efree(cache_entries); + if (rval) { + efree(rval); + } + + } + + /* 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(filters) = filters; + APCG(cache_by_default) = cache_by_default; + + APCG(current_cache) = NULL; + HANDLE_UNBLOCK_INTERRUPTIONS(); + +} +/* }}} */ + +/* {{{ proto mixed apc_bin_dump([array files [, array user_vars]]) + Returns a binary dump of the given files and user variables from the APC cache. + A NULL for files or user_vars signals a dump of every entry, while array() will dump nothing. + */ +PHP_FUNCTION(apc_bin_dump) { + + zval *z_files = NULL, *z_user_vars = NULL; + HashTable *h_files, *h_user_vars; + apc_bd_t *bd; + + if(!APCG(enabled)) { + apc_warning("APC is not enabled, apc_bin_dump not available." TSRMLS_CC); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!a!", &z_files, &z_user_vars) == FAILURE) { + return; + } + + h_files = z_files ? Z_ARRVAL_P(z_files) : NULL; + h_user_vars = z_user_vars ? Z_ARRVAL_P(z_user_vars) : NULL; + bd = apc_bin_dump(h_files, h_user_vars TSRMLS_CC); + if(bd) { + RETVAL_STRINGL((char*)bd, bd->size-1, 0); + } else { + apc_error("Unknown error encountered during apc_bin_dump." TSRMLS_CC); + RETVAL_NULL(); + } + + return; +} + +/* {{{ proto mixed apc_bin_dumpfile(array files, array user_vars, string filename, [int flags [, resource context]]) + Output a binary dump of the given files and user variables from the APC cache to the named file. + */ +PHP_FUNCTION(apc_bin_dumpfile) { + + zval *z_files = NULL, *z_user_vars = NULL; + HashTable *h_files, *h_user_vars; + char *filename = NULL; + int filename_len; + long flags=0; + zval *zcontext = NULL; + php_stream_context *context = NULL; + php_stream *stream; + int numbytes = 0; + apc_bd_t *bd; + + if(!APCG(enabled)) { + apc_warning("APC is not enabled, apc_bin_dumpfile not available." TSRMLS_CC); + RETURN_FALSE; + } + + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!s|lr!", &z_files, &z_user_vars, &filename, &filename_len, &flags, &zcontext) == FAILURE) { + return; + } + + if(!filename_len) { + apc_error("apc_bin_dumpfile filename argument must be a valid filename." TSRMLS_CC); + RETURN_FALSE; + } + + h_files = z_files ? Z_ARRVAL_P(z_files) : NULL; + h_user_vars = z_user_vars ? Z_ARRVAL_P(z_user_vars) : NULL; + bd = apc_bin_dump(h_files, h_user_vars TSRMLS_CC); + if(!bd) { + apc_error("Unknown error encountered during apc_bin_dumpfile." TSRMLS_CC); + RETURN_FALSE; + } + + + /* Most of the following has been taken from the file_get/put_contents functions */ + + context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); + stream = php_stream_open_wrapper_ex(filename, (flags & PHP_FILE_APPEND) ? "ab" : "wb", + ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); + if (stream == NULL) { + efree(bd); + apc_error("Unable to write to file in apc_bin_dumpfile." TSRMLS_CC); + RETURN_FALSE; + } + + if (flags & LOCK_EX && php_stream_lock(stream, LOCK_EX)) { + php_stream_close(stream); + efree(bd); + apc_error("Unable to get a lock on file in apc_bin_dumpfile." TSRMLS_CC); + RETURN_FALSE; + } + + numbytes = php_stream_write(stream, (char*)bd, bd->size); + if(numbytes != bd->size) { + numbytes = -1; + } + + php_stream_close(stream); + efree(bd); + + if(numbytes < 0) { + apc_error("Only %d of %d bytes written, possibly out of free disk space" TSRMLS_CC, numbytes, bd->size); + RETURN_FALSE; + } + + RETURN_LONG(numbytes); +} + +/* {{{ proto mixed apc_bin_load(string data, [int flags]) + Load the given binary dump into the APC file/user cache. + */ +PHP_FUNCTION(apc_bin_load) { + + int data_len; + char *data; + long flags = 0; + + if(!APCG(enabled)) { + apc_warning("APC is not enabled, apc_bin_load not available." TSRMLS_CC); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &flags) == FAILURE) { + return; + } + + if(!data_len || data_len != ((apc_bd_t*)data)->size -1) { + apc_error("apc_bin_load string argument does not appear to be a valid APC binary dump due to size (%d vs expected %d)." TSRMLS_CC, data_len, ((apc_bd_t*)data)->size -1); + RETURN_FALSE; + } + + apc_bin_load((apc_bd_t*)data, (int)flags TSRMLS_CC); + + RETURN_TRUE; +} + +/* {{{ proto mixed apc_bin_loadfile(string filename, [resource context, [int flags]]) + Load the given binary dump from the named file into the APC file/user cache. + */ +PHP_FUNCTION(apc_bin_loadfile) { + + char *filename; + int filename_len; + zval *zcontext = NULL; + long flags; + php_stream_context *context = NULL; + php_stream *stream; + char *data; + int len; + + if(!APCG(enabled)) { + apc_warning("APC is not enabled, apc_bin_loadfile not available." TSRMLS_CC); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r!l", &filename, &filename_len, &zcontext, &flags) == FAILURE) { + return; + } + + if(!filename_len) { + apc_error("apc_bin_loadfile filename argument must be a valid filename." TSRMLS_CC); + RETURN_FALSE; + } + + context = php_stream_context_from_zval(zcontext, 0); + stream = php_stream_open_wrapper_ex(filename, "rb", + ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); + if (!stream) { + apc_error("Unable to read from file in apc_bin_loadfile." TSRMLS_CC); + RETURN_FALSE; + } + + len = php_stream_copy_to_mem(stream, &data, PHP_STREAM_COPY_ALL, 0); + if(len == 0) { + apc_warning("File passed to apc_bin_loadfile was empty: %s." TSRMLS_CC, filename); + RETURN_FALSE; + } else if(len < 0) { + apc_warning("Error reading file passed to apc_bin_loadfile: %s." TSRMLS_CC, filename); + RETURN_FALSE; + } else if(len != ((apc_bd_t*)data)->size) { + apc_warning("file passed to apc_bin_loadfile does not appear to be valid due to size (%d vs expected %d)." TSRMLS_CC, len, ((apc_bd_t*)data)->size -1); + RETURN_FALSE; + } + php_stream_close(stream); + + apc_bin_load((apc_bd_t*)data, (int)flags TSRMLS_CC); + efree(data); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ arginfo */ +#if (PHP_MAJOR_VERSION >= 6 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3)) +# define PHP_APC_ARGINFO +#else +# define PHP_APC_ARGINFO static +#endif + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_store, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, var) + ZEND_ARG_INFO(0, ttl) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_clear_cache, 0, 0, 0) + ZEND_ARG_INFO(0, info) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_sma_info, 0, 0, 0) + ZEND_ARG_INFO(0, limited) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_cache_info, 0, 0, 0) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, limited) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_define_constants, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, constants) + ZEND_ARG_INFO(0, case_sensitive) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO(arginfo_apc_delete_file, 0) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO(arginfo_apc_delete, 0) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_fetch, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(1, success) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_inc, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, step) + ZEND_ARG_INFO(1, success) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO(arginfo_apc_cas, 0) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, old) + ZEND_ARG_INFO(0, new) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_load_constants, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, case_sensitive) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_compile_file, 0, 0, 1) + ZEND_ARG_INFO(0, filenames) + ZEND_ARG_INFO(0, atomic) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_bin_dump, 0, 0, 0) + ZEND_ARG_INFO(0, files) + ZEND_ARG_INFO(0, user_vars) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_bin_dumpfile, 0, 0, 3) + ZEND_ARG_INFO(0, files) + ZEND_ARG_INFO(0, user_vars) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, context) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_bin_load, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_apc_bin_loadfile, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, context) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +PHP_APC_ARGINFO +ZEND_BEGIN_ARG_INFO(arginfo_apc_exists, 0) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ apc_functions[] */ +zend_function_entry apc_functions[] = { + PHP_FE(apc_cache_info, arginfo_apc_cache_info) + PHP_FE(apc_clear_cache, arginfo_apc_clear_cache) + PHP_FE(apc_sma_info, arginfo_apc_sma_info) + PHP_FE(apc_store, arginfo_apc_store) + PHP_FE(apc_fetch, arginfo_apc_fetch) + PHP_FE(apc_delete, arginfo_apc_delete) + PHP_FE(apc_delete_file, arginfo_apc_delete_file) + PHP_FE(apc_define_constants, arginfo_apc_define_constants) + PHP_FE(apc_load_constants, arginfo_apc_load_constants) + PHP_FE(apc_compile_file, arginfo_apc_compile_file) + PHP_FE(apc_add, arginfo_apc_store) + PHP_FE(apc_inc, arginfo_apc_inc) + PHP_FE(apc_dec, arginfo_apc_inc) + PHP_FE(apc_cas, arginfo_apc_cas) + PHP_FE(apc_bin_dump, arginfo_apc_bin_dump) + PHP_FE(apc_bin_load, arginfo_apc_bin_load) + PHP_FE(apc_bin_dumpfile, arginfo_apc_bin_dumpfile) + PHP_FE(apc_bin_loadfile, arginfo_apc_bin_loadfile) + PHP_FE(apc_exists, arginfo_apc_exists) + {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), + PHP_APC_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_APC +ZEND_GET_MODULE(apc) +#endif +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/php_apc.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2010 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 | + | George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + 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.h 305846 2010-11-30 09:36:57Z gopalv $ */ + +#ifndef PHP_APC_H +#define PHP_APC_H + +#include "apc_php.h" +#include "apc_globals.h" + +#define PHP_APC_VERSION "3.1.6" + +extern zend_module_entry apc_module_entry; +#define apc_module_ptr &apc_module_entry + +#define phpext_apc_ptr apc_module_ptr + +#endif /* PHP_APC_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ --- /dev/null +++ b/ext/apc/TECHNOTES.txt @@ -0,0 +1,361 @@ +APC Quick-Start Braindump + +This is a rapidly written braindump of how APC currently works in the +form of a quick-start guide to start hacking on APC. + +1. Install and use APC a bit so you know what it does from the end-user's + perspective. + user-space functions are all explained here: + +2. Grab the current APC code from CVS: + + cvs -d:pserver:cvsread@cvs.php.net:/repository login + Password: phpfi + cvs -d:pserver:cvsread@cvs.php.net:/repository co pecl/apc + + apc/php_apc.c has most of the code for the user-visible stuff. It is + also a regular PHP extension in the sense that there are MINIT, MINFO, + MSHUTDOWN, RSHUTDOWN, etc. functions. + +3. Build it. + + cd pecl/apc + phpize + ./configure --enable-apc --enable-mmap + make + cp modules/apc.so /usr/local/lib/php + apachectl restart + +4. Debugging Hints + + apachectl stop + gdb /usr/bin/httpd + break ?? + run -X + + Grab the .gdbinit from the PHP source tree and have a look at the macros. + +5. Look through apc/apc_sma.c + It is a pretty standard memory allocator. + + apc_sma_malloc, apc_sma_realloc, apc_sma_strdup and apc_sma_free behave to the + caller just like malloc, realloc, strdup and free + + On server startup the MINIT hook in php_apc.c calls apc_module_init() in + apc_main.c which in turn calls apc_sma_init(). apc_sma_init calls into + apc_mmap.c to mmap the specified sized segment (I tend to just use a single + segment). apc_mmap.c should be self-explanatory. It mmaps a temp file and + then unlinks that file right after the mmap to provide automatic shared memory + cleanup in case the process dies. + + Once the region has been initialized we stick a header_t at the beginning + of the region. It contains the total size in header->segsize and the number + of bytes available in header->avail. + + After the header comes a bit of a hack. A zero-sized block is inserted just + to make things easier later on. And then a huge block that is basically + the size of the entire segment minus the two (for the 0-sized block, and this one) + block headers. + + The code for this is: + + header = (header_t*) shmaddr; + header->segsize = sma_segsize; + header->avail = sma_segsize - sizeof(header_t) - sizeof(block_t) - alignword(sizeof(int)); + memset(&header->lock,0,sizeof(header->lock)); + sma_lock = &header->lock; + block = BLOCKAT(sizeof(header_t)); + block->size = 0; + block->next = sizeof(header_t) + sizeof(block_t); + block = BLOCKAT(block->next); + block->size = header->avail; + block->next = 0; + + So the shared memory looks like this: + + +--------+-------+---------------------------------+ + | header | block | block | + +--------+-------+---------------------------------+ + + sma_shmaddrs[0] gives you the address of header + + The blocks are just a simple offset-based linked list (so no pointers): + + typedef struct block_t block_t; + struct block_t { + size_t size; /* size of this block */ + size_t next; /* offset in segment of next free block */ + size_t canary; /* canary to check for memory overwrites */ +#ifdef __APC_SMA_DEBUG__ + int id; /* identifier for the memory block */ +#endif + }; + + The BLOCKAT macro turns an offset into an actual address for you: + + #define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset)) + + where shmaddr = sma_shaddrs[0] + + And the OFFSET macro goes the other way: + + #define OFFSET(block) ((int)(((char*)block) - (char*)shmaddr)) + + Allocating a block with a call to apc_sma_allocate() walks through the + linked list of blocks until it finds one that is >= to the requested size. + The first call to apc_sma_allocate() will hit the second block. We then + chop up that block so it looks like this: + + +--------+-------+-------+-------------------------+ + | header | block | block | block | + +--------+-------+-------+-------------------------+ + + Then we unlink that block from the linked list so it won't show up + as an available block on the next allocate. So we actually have: + + +--------+-------+ +-------------------------+ + | header | block |------>| block | + +--------+-------+ +-------------------------+ + + And header->avail along with block->size of the remaining large + block are updated accordingly. The arrow there representing the + link which now points to a block with an offset further along in + the segment. + + When the block is freed using apc_sma_deallocate() the steps are + basically just reversed. The block is put back and then the deallocate + code looks at the block before and after to see if the block immediately + before and after are free and if so the blocks are combined. So you never + have 2 free blocks next to each other, apart from at the front with that + 0-sized dummy block. This mostly prevents fragmentation. I have been + toying with the idea of always allocating block at 2^n boundaries to make + it more likely that they will be re-used to cut down on fragmentation further. + That's what the POWER_OF_TWO_BLOCKSIZE you see in apc_sma.c is all about. + + Of course, anytime we fiddle with our shared memory segment we lock using + the locking macros, LOCK() and UNLOCK(). + + That should mostly take care of the low-level shared memory handling. + +6. Next up is apc_main.c and apc_cache.c which implement the meat of the + cache logic. + + The apc_main.c file mostly calls functions in apc_sma.c to allocate memory + and apc_cache.c for actual cache manipulation. + + After the shared memory segment is created and the caches are initialized, + apc_module_init() installs the my_compile_file() function overriding Zend's + version. I'll talk about my_compile_file() and the rest of apc_compile.c + in the next section. For now I will stick with apc_main.c and apc_cache.c + and talk about the actual caches. A cache consists of a block of shared + memory returned by apc_sma_allocate() via apc_sma_malloc(). You will + notice references to apc_emalloc(). apc_emalloc() is just a thin wrapper + around PHP's own emalloc() function which allocates per-process memory from + PHP's pool-based memory allocator. Don't confuse apc_emalloc() and + apc_sma_malloc() as the first is per-process and the second is shared memory. + + The cache is stored in/described by this struct allocated locally using + emalloc(): + + struct apc_cache_t { + void* shmaddr; /* process (local) address of shared cache */ + header_t* header; /* cache header (stored in SHM) */ + slot_t** slots; /* array of cache slots (stored in SHM) */ + int num_slots; /* number of slots in cache */ + int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ + }; + + Whenever you see functions that take a 'cache' argument, this is what they + take. And apc_cache_create() returns a pointer to this populated struct. + + At the beginning of the cache we have a header. Remember, we are down a level now + from the sma stuff. The sma stuff is the low-level shared-memory allocator which + has its own header which is completely separate and invisible to apc_cache.c. + As far as apc_cache.c is concerned the block of memory it is working with could + have come from a call to malloc(). + + The header looks like this: + + typedef struct header_t header_t; + struct header_t { + int num_hits; /* total successful hits in cache */ + int num_misses; /* total unsuccessful hits in cache */ + slot_t* deleted_list; /* linked list of to-be-deleted slots */ + }; + + Since this is at the start of the shared memory segment, these values are accessible + across all the yapache processes and hence access to them has to be locked. + + After the header we have an array of slots. The number of slots is user-defined + through the apc.num_slots ini hint. Each slot is described by: + + typedef struct slot_t slot_t; + struct slot_t { + apc_cache_key_t key; /* slot key */ + apc_cache_entry_t* value; /* slot value */ + slot_t* next; /* next slot in linked list */ + int num_hits; /* number of hits to this bucket */ + time_t creation_time; /* time slot was initialized */ + time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ + }; + + The slot_t *next there is a linked list to other slots that happened to hash to the + same array position. + + apc_cache_insert() shows what happens on a new cache insert. + + slot = &cache->slots[hash(key) % cache->num_slots]; + + cache->slots is our array of slots in the segment. hash() is simply: + + static unsigned int hash(apc_cache_key_t key) + { + return key.data.file.device + key.data.file.inode; + } + + That is, we use the file's device and inode to uniquely identify it. Initially + we had used the file's full path, but getting that requires a realpath() call which + is amazingly expensive since it has to stat each component of the path to resolve + symlinks and get rid of relative path components. By using the device+inode we + can uniquely identify a file with a single stat. + + So, on an insert we find the array position in the slots array by hashing the device+inode. + If there are currently no other slots there, we just create the slot and stick it into + the array: + + *slot = make_slot(key, value, *slot, t) + + If there are other slots already at this position we walk the link list to get to + the end. Here is the loop: + + while (*slot) { + if (key_equals((*slot)->key.data.file, key.data.file)) { + /* If existing slot for the same device+inode is different, remove it and insert the new version */ + if ((*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot); + break; + } + UNLOCK(cache); + return 0; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot); + continue; + } + slot = &(*slot)->next; + } + + That first key_equals() check sees if we have an exact match meaning the file + is already in the cache. Since we try to find the file in the cache before doing + an insert, this will generally only happen if another process managed to beat us + to inserting it. If we have a newer version of the file at this point we remove + it an insert the new version. If our version is not newer we just return without + doing anything. + + While walking the linked list we also check to see if the cache has a TTL defined. + If while walking the linked list we see a slot that has expired, we remove it + since we are right there looking at it. This is the only place we remove stale + entries unless the shared memory segment fills up and we force a full expunge via + apc_cache_expunge(). apc_cache_expunge() walks the entire slots array and walks + down every linked list removing stale slots to free up room. This is obviously + slow and thus only happens when we have run out of room. + + apc_cache_find() simply hashes and returns the entry if it is there. If it is there + but older than the mtime in the entry we are looking for, we delete the one that is + there and return indicating we didn't find it. + + Next we need to understand what an actual cache entry looks like. Have a look at + apc_cache.h for the structs. I sort of glossed over the key part earlier saying + that we just used the device+inode to find a hash slot. It is actually a bit more + complex than that because we have two kinds of caches. We have the standard file + cache containing opcode arrays, but we also have a user-controlled cache that the + user can insert whatever they want into via apc_store(). For the user cache we + obviously don't have a device+inode. The actual identifier is provided by the user + as a char *. So the key is actually a union that looks like this: + + typedef union _apc_cache_key_data_t { + struct { + int device; /* the filesystem device */ + int inode; /* the filesystem inode */ + } file; + struct { + char *identifier; + } user; + } apc_cache_key_data_t; + + struct apc_cache_key_t { + apc_cache_key_data_t data; + int mtime; /* the mtime of this cached entry */ + }; + + And we have two sets of functions to do inserts and finds. apc_cache_user_find() + and apc_cache_user_insert() operate on the user cache. + + Ok, on to the actual cache entry. Again, because we have two kinds of caches, we + also have the corresponding two kinds of cache entries described by this union: + + typedef union _apc_cache_entry_value_t { + struct { + char *filename; /* absolute path to source file */ + zend_op_array* op_array; /* op_array allocated in shared memory */ + apc_function_t* functions; /* array of apc_function_t's */ + apc_class_t* classes; /* array of apc_class_t's */ + } file; + struct { + char *info; + zval *val; + unsigned int ttl; + } user; + } apc_cache_entry_value_t; + + And then the actual cache entry: + + struct apc_cache_entry_t { + apc_cache_entry_value_t data; + unsigned char type; + int ref_count; + }; + + The user entry is pretty simple and not all that important for now. I will + concentrate on the file entries since that is what holds the actual compiled + opcode arrays along with the functions and classes required by the executor. + + apc_cache_make_file_entry() in apc_cache.c shows how an entry is constructed. + The main thing to understand here is that we need more than just the opcode + array, we also need the functions and classes created by the compiler when it + created the opcode array. As far as the executor is concerned, it doesn't know + that it isn't operating in normal mode being called right after the parse/compile + phase, so we need to recreate everything so it looks exactly like it would at + that point. + +7. my_compile_file() and apc_compile.c + + my_compile_file() in apc_main.c controls where we get the opcodes from. If + the user-specified filters exclude the file from being cached, then we just + call the original compile function and return. Otherwise we fetch the request + time from Apache to avoid an extra syscall, create the key so we can look up + the file in the cache. If we find it we stick it on a local stack which we + use at cleanup time to make sure we return everything back to normal after a + request and call cached_compile() which installs the functions and classes + associated with the op_array in this entry and then copy the op_array down + into our memory space for execution. + + If we didn't find the file in the cache, we need to compile it and insert it. + To compile it we simply call the original compile function: + + op_array = old_compile_file(h, type TSRMLS_CC); + + To do the insert we need to copy the functions, classes and the opcode array + the compile phase created into shared memory. This all happens in apc_compile.c + in the apc_copy_op_array(), apc_copy_new_functions() and apc_copy_new_classes() + functions. Then we make the file entry and do the insert. Both of these + operations were described in the previous section. + +8. The Optimizer + + The optimizer has been deprecated. + +If you made it to the end of this, you should have a pretty good idea of where things are in +the code. I skimmed over a lot of things, so plan on spending some time reading through the code. + --- /dev/null +++ b/ext/apc/tests/apc_001.phpt @@ -0,0 +1,32 @@ +--TEST-- +APC: apc_store/fetch with strings +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +string(11) "hello world" +string(11) "hello world" +string(4) "nice" +string(11) "hello world" +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_002.phpt @@ -0,0 +1,34 @@ +--TEST-- +APC: apc_store/fetch with objects +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +a = true; +var_dump($bar); + +?> +===DONE=== + +--EXPECTF-- +object(foo)#%d (0) { +} +object(foo)#%d (0) { +} +object(foo)#%d (1) { + ["a"]=> + bool(true) +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_003b.phpt @@ -0,0 +1,117 @@ +--TEST-- +APC: apc_store/fetch with objects (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +a = true; +var_dump($bar); + +class bar extends foo +{ + public $pub = 'bar'; + protected $pro = 'bar'; + private $pri = 'bar'; // we don't see this, we'd need php 5.1 new serialization + + function __construct() + { + $this->bar = true; + } + + function change() + { + $this->pri = 'mod'; + } +} + +class baz extends bar +{ + private $pri = 'baz'; + + function __construct() + { + parent::__construct(); + $this->baz = true; + } +} + +$baz = new baz; +var_dump($baz); +$baz->change(); +var_dump($baz); +apc_store('baz', $baz); +unset($baz); +var_dump(apc_fetch('baz')); + +?> +===DONE=== + +--EXPECTF-- +object(foo)#%d (0) { +} +object(foo)#%d (0) { +} +object(foo)#%d (1) { + ["a"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri":"baz":private]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro":protected]=> + string(3) "bar" + ["pri":"bar":private]=> + string(3) "bar" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri":"baz":private]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro":protected]=> + string(3) "bar" + ["pri":"bar":private]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri":"baz":private]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro":protected]=> + string(3) "bar" + ["pri":"bar":private]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_003.phpt @@ -0,0 +1,117 @@ +--TEST-- +APC: apc_store/fetch with objects (php pre-5.3) +--SKIPIF-- += 0) { + echo "skip\n"; + } +?> +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +a = true; +var_dump($bar); + +class bar extends foo +{ + public $pub = 'bar'; + protected $pro = 'bar'; + private $pri = 'bar'; // we don't see this, we'd need php 5.1 new serialization + + function __construct() + { + $this->bar = true; + } + + function change() + { + $this->pri = 'mod'; + } +} + +class baz extends bar +{ + private $pri = 'baz'; + + function __construct() + { + parent::__construct(); + $this->baz = true; + } +} + +$baz = new baz; +var_dump($baz); +$baz->change(); +var_dump($baz); +apc_store('baz', $baz); +unset($baz); +var_dump(apc_fetch('baz')); + +?> +===DONE=== + +--EXPECTF-- +object(foo)#%d (0) { +} +object(foo)#%d (0) { +} +object(foo)#%d (1) { + ["a"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "bar" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_004.phpt @@ -0,0 +1,38 @@ +--TEST-- +APC: apc_store/fetch with bools +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +bool(false) +bool(false) +bool(false) +bool(true) +bool(false) +bool(false) +bool(false) +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_005.phpt @@ -0,0 +1,50 @@ +--TEST-- +APC: apc_store/fetch with arrays of objects +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +array(2) { + [0]=> + object(stdClass)#1 (0) { + } + [1]=> + object(stdClass)#2 (0) { + } +} +array(2) { + [0]=> + object(stdClass)#1 (0) { + } + [1]=> + object(stdClass)#2 (0) { + } +} +array(2) { + [0]=> + object(stdClass)#3 (0) { + } + [1]=> + object(stdClass)#4 (0) { + } +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_006.phpt @@ -0,0 +1,72 @@ +--TEST-- +APC: apc_store/fetch reference test +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +report_memleaks=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +array(9) refcount(2){ + [0]=> + string(1) "a" refcount(1) + [1]=> + &array(1) refcount(2){ + [0]=> + string(1) "c" refcount(1) + } + [2]=> + &array(1) refcount(2){ + [0]=> + string(1) "c" refcount(1) + } + [3]=> + &string(1) "d" refcount(3) + [4]=> + &string(1) "d" refcount(3) + [5]=> + &string(1) "d" refcount(3) + [6]=> + string(1) "e" refcount(2) + [7]=> + string(1) "e" refcount(2) + [8]=> + &array(2) refcount(2){ + [0]=> + string(1) "f" refcount(1) + [1]=> + &array(2) refcount(2){ + [0]=> + string(1) "f" refcount(1) + [1]=> + *RECURSION* + } + } +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_007.phpt @@ -0,0 +1,46 @@ +--TEST-- +APC: apc_inc/apc_dec test +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +$foobar = 2 +$foobar += 1 = 3 +$foobar += 10 = 13 +$foobar -= 1 = 12 +$foobar -= 10 = 2 +$f__bar += 1 = fail +$perfection -= 1 = epic fail +$foobar += 1 = 3 +pass by ref success 1 +$foobar -= 1 = 2 +pass by ref success 1 +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_008.phpt @@ -0,0 +1,34 @@ +--TEST-- +APC: apc_cas test +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +$foobar = 2 +$foobar == 1 ? 2 : 1 = fail +$foobar == 2 ? 1 : 2 = ok +$foobar = 1 +$f__bar == 1 ? 2 : 1 = fail +$perfection == 2 ? 1 : 2 = epic fail +$foobar = 1 +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_009.phpt @@ -0,0 +1,97 @@ +--TEST-- +APC: apc_delete_file test +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +apc.stat=On +report_memleaks=0 +--FILE-- + +===DONE=== + +--CLEAN-- + +--EXPECTF-- +apc_009.php Found File +apc_009.php Not Found +apc_009.php Not Found +apc_009.php Not Found +array(0) { +} +apc_009.php Found File +apc_009-1.php Found File + +Parse error: syntax error, unexpected '!' in %s/apc_009-2.php on line 1 + +Warning: apc_compile_file(): Error compiling apc_009-2.php in apc_compile_file. in %s/apc_009.php on line 29 + +Warning: apc_compile_file(): Error compiling nofile.php in apc_compile_file. in %s/apc_009.php on line 29 +array(2) { + ["apc_009-2.php"]=> + int(-1) + ["nofile.php"]=> + int(-1) +} +apc_009.php Found File +apc_009-1.php Found File +apc_009-2.php Not Found +nofile.php Not Found +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_010.phpt @@ -0,0 +1,83 @@ +--TEST-- +APC: apc_store/fetch/add with array of key/value pairs. +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +array(0) { +} +array(4) { + ["key1"]=> + string(6) "value1" + ["key2"]=> + string(6) "value2" + ["key3"]=> + array(2) { + [0]=> + string(7) "value3a" + [1]=> + string(7) "value3b" + } + ["key4"]=> + int(4) +} +array(2) { + ["key1"]=> + string(6) "value1" + ["key3"]=> + array(2) { + [0]=> + string(7) "value3a" + [1]=> + string(7) "value3b" + } +} +array(2) { + ["key1"]=> + int(-1) + ["key3"]=> + int(-1) +} +array(4) { + ["key1"]=> + string(6) "value1" + ["key2"]=> + string(6) "value2" + ["key3"]=> + array(2) { + [0]=> + string(7) "value3a" + [1]=> + string(7) "value3b" + } + ["key4"]=> + int(4) +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc53_001.phpt @@ -0,0 +1,33 @@ +--TEST-- +APC: classes with namespaces (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +object(Foo\Bar\Baz)#1 (3) { + ["i"]=> + int(1) + ["f":protected]=> + float(3.14) + ["s":"Foo\Bar\Baz":private]=> + string(11) "hello world" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc53_002.phpt @@ -0,0 +1,47 @@ +--TEST-- +APC: global spaces (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +foo(); +var_dump(Foo\Bar\sort()); +?> +===DONE=== + +--EXPECTF-- +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) +} +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) +} +string(8) "IT WORKS" +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc53_003.phpt @@ -0,0 +1,31 @@ +--TEST-- +APC: anonymous functions (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +Hello World +Hello PHP +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc53_004.phpt @@ -0,0 +1,33 @@ +--TEST-- +APC: closures (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +double of 9 is 18 +triple of 4 is 12 +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc53_005.phpt @@ -0,0 +1,35 @@ +--TEST-- +APC: goto (php 5.3) +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +1 +2 +4 +5 +7 +8 +10 +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_bin_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +APC: bindump user cache +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +--FILE-- + +===DONE=== + +--EXPECTF-- +bool(false) +string(9) "testvalue" +===DONE=== --- /dev/null +++ b/ext/apc/tests/apc_bin_002-1.inc @@ -0,0 +1,42 @@ +my_method()."\n"; +echo "class static property: ".my_class::$my_static_property."\n"; +echo "class dynamic property: ".$my_class->my_property."\n"; +echo "class constant: ".my_class::my_constant."\n"; +echo "\n"; +echo "inherited class static method: ".my_i_class::my_static_method()."\n"; +echo "inherited class dynamic method: ".$my_i_class->my_method()."\n"; +echo "inherited class static property: ".my_i_class::$my_static_property."\n"; +echo "inherited class dynamic property: ".$my_i_class->my_property."\n"; +echo "inherited class constant: ".my_i_class::my_constant."\n"; +echo "\n"; + + + +function my_function() { return "Success"; } + + +class my_class { + static $my_static_property = "Success"; + var $my_property = "Success"; + const my_constant = "Success"; + static function my_static_method() { return "Success"; } + function my_method() { return "Success"; } +} + +class my_i_class extends my_class { + function dummy() { return 1; } +} + --- /dev/null +++ b/ext/apc/tests/apc_bin_002-2.inc @@ -0,0 +1,5 @@ + --- /dev/null +++ b/ext/apc/tests/apc_bin_002.phpt @@ -0,0 +1,53 @@ +--TEST-- +APC: bindump file cache part 1 +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.stat=0 +apc.cache_by_default=1 +apc.filters= +report_memleaks = Off +--FILE-- + +===DONE=== + +--EXPECTF-- +apc bindump 002 test + +global scope execution: Success + +function execution: Success + +class static method: Success +class dynamic method: Success +class static property: Success +class dynamic property: Success +class constant: Success + +inherited class static method: Success +inherited class dynamic method: Success +inherited class static property: Success +inherited class dynamic property: Success +inherited class constant: Success + +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_001.phpt @@ -0,0 +1,110 @@ +--TEST-- +APC: APCIterator general +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +$value) { + $vals[$key] = $value['key']; +} +ksort($vals); +var_dump($vals); + +?> +===DONE=== + +--EXPECT-- +array(41) { + ["key0"]=> + string(4) "key0" + ["key1"]=> + string(4) "key1" + ["key10"]=> + string(5) "key10" + ["key11"]=> + string(5) "key11" + ["key12"]=> + string(5) "key12" + ["key13"]=> + string(5) "key13" + ["key14"]=> + string(5) "key14" + ["key15"]=> + string(5) "key15" + ["key16"]=> + string(5) "key16" + ["key17"]=> + string(5) "key17" + ["key18"]=> + string(5) "key18" + ["key19"]=> + string(5) "key19" + ["key2"]=> + string(4) "key2" + ["key20"]=> + string(5) "key20" + ["key21"]=> + string(5) "key21" + ["key22"]=> + string(5) "key22" + ["key23"]=> + string(5) "key23" + ["key24"]=> + string(5) "key24" + ["key25"]=> + string(5) "key25" + ["key26"]=> + string(5) "key26" + ["key27"]=> + string(5) "key27" + ["key28"]=> + string(5) "key28" + ["key29"]=> + string(5) "key29" + ["key3"]=> + string(4) "key3" + ["key30"]=> + string(5) "key30" + ["key31"]=> + string(5) "key31" + ["key32"]=> + string(5) "key32" + ["key33"]=> + string(5) "key33" + ["key34"]=> + string(5) "key34" + ["key35"]=> + string(5) "key35" + ["key36"]=> + string(5) "key36" + ["key37"]=> + string(5) "key37" + ["key38"]=> + string(5) "key38" + ["key39"]=> + string(5) "key39" + ["key4"]=> + string(4) "key4" + ["key40"]=> + string(5) "key40" + ["key5"]=> + string(4) "key5" + ["key6"]=> + string(4) "key6" + ["key7"]=> + string(4) "key7" + ["key8"]=> + string(4) "key8" + ["key9"]=> + string(4) "key9" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_002.phpt @@ -0,0 +1,36 @@ +--TEST-- +APC: APCIterator regex +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +$value) { + $vals[$key] = $value['key']; +} +ksort($vals); +var_dump($vals); + +?> +===DONE=== + +--EXPECT-- +array(4) { + ["key10"]=> + string(5) "key10" + ["key20"]=> + string(5) "key20" + ["key30"]=> + string(5) "key30" + ["key40"]=> + string(5) "key40" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_003.phpt @@ -0,0 +1,110 @@ +--TEST-- +APC: APCIterator chunk size +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +$value) { + $vals[$key] = $value['key']; +} +ksort($vals); +var_dump($vals); + +?> +===DONE=== + +--EXPECT-- +array(41) { + ["key0"]=> + string(4) "key0" + ["key1"]=> + string(4) "key1" + ["key10"]=> + string(5) "key10" + ["key11"]=> + string(5) "key11" + ["key12"]=> + string(5) "key12" + ["key13"]=> + string(5) "key13" + ["key14"]=> + string(5) "key14" + ["key15"]=> + string(5) "key15" + ["key16"]=> + string(5) "key16" + ["key17"]=> + string(5) "key17" + ["key18"]=> + string(5) "key18" + ["key19"]=> + string(5) "key19" + ["key2"]=> + string(4) "key2" + ["key20"]=> + string(5) "key20" + ["key21"]=> + string(5) "key21" + ["key22"]=> + string(5) "key22" + ["key23"]=> + string(5) "key23" + ["key24"]=> + string(5) "key24" + ["key25"]=> + string(5) "key25" + ["key26"]=> + string(5) "key26" + ["key27"]=> + string(5) "key27" + ["key28"]=> + string(5) "key28" + ["key29"]=> + string(5) "key29" + ["key3"]=> + string(4) "key3" + ["key30"]=> + string(5) "key30" + ["key31"]=> + string(5) "key31" + ["key32"]=> + string(5) "key32" + ["key33"]=> + string(5) "key33" + ["key34"]=> + string(5) "key34" + ["key35"]=> + string(5) "key35" + ["key36"]=> + string(5) "key36" + ["key37"]=> + string(5) "key37" + ["key38"]=> + string(5) "key38" + ["key39"]=> + string(5) "key39" + ["key4"]=> + string(4) "key4" + ["key40"]=> + string(5) "key40" + ["key5"]=> + string(4) "key5" + ["key6"]=> + string(4) "key6" + ["key7"]=> + string(4) "key7" + ["key8"]=> + string(4) "key8" + ["key9"]=> + string(4) "key9" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_004.phpt @@ -0,0 +1,36 @@ +--TEST-- +APC: APCIterator regex & chunk size & list +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +$value) { + $vals[$key] = $value['key']; +} +ksort($vals); +var_dump($vals); + +?> +===DONE=== + +--EXPECT-- +array(4) { + ["key10"]=> + string(5) "key10" + ["key20"]=> + string(5) "key20" + ["key30"]=> + string(5) "key30" + ["key40"]=> + string(5) "key40" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_005.phpt @@ -0,0 +1,112 @@ +--TEST-- +APC: APCIterator delete +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +$value) { + $vals[$key] = $value['key']; +} +foreach($it2 as $key=>$value) { + $vals2[$key] = $value['key']; +} +ksort($vals2); +var_dump($vals); +var_dump($vals2); + +?> +===DONE=== + +--EXPECT-- +array(0) { +} +array(37) { + ["key0"]=> + string(4) "key0" + ["key1"]=> + string(4) "key1" + ["key11"]=> + string(5) "key11" + ["key12"]=> + string(5) "key12" + ["key13"]=> + string(5) "key13" + ["key14"]=> + string(5) "key14" + ["key15"]=> + string(5) "key15" + ["key16"]=> + string(5) "key16" + ["key17"]=> + string(5) "key17" + ["key18"]=> + string(5) "key18" + ["key19"]=> + string(5) "key19" + ["key2"]=> + string(4) "key2" + ["key21"]=> + string(5) "key21" + ["key22"]=> + string(5) "key22" + ["key23"]=> + string(5) "key23" + ["key24"]=> + string(5) "key24" + ["key25"]=> + string(5) "key25" + ["key26"]=> + string(5) "key26" + ["key27"]=> + string(5) "key27" + ["key28"]=> + string(5) "key28" + ["key29"]=> + string(5) "key29" + ["key3"]=> + string(4) "key3" + ["key31"]=> + string(5) "key31" + ["key32"]=> + string(5) "key32" + ["key33"]=> + string(5) "key33" + ["key34"]=> + string(5) "key34" + ["key35"]=> + string(5) "key35" + ["key36"]=> + string(5) "key36" + ["key37"]=> + string(5) "key37" + ["key38"]=> + string(5) "key38" + ["key39"]=> + string(5) "key39" + ["key4"]=> + string(4) "key4" + ["key5"]=> + string(4) "key5" + ["key6"]=> + string(4) "key6" + ["key7"]=> + string(4) "key7" + ["key8"]=> + string(4) "key8" + ["key9"]=> + string(4) "key9" +} +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_006.phpt @@ -0,0 +1,1535 @@ +--TEST-- +APC: APCIterator formats +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + $format) { + $it_array[$idx] = new APCIterator('user', NULL, $format); +} + +for($i = 0; $i < 11; $i++) { + apc_store("key$i", "value$i"); +} + +foreach ($it_array as $idx => $it) { + print_it($it, $idx); +} + +function print_it($it, $idx) { + echo "IT #$idx\n"; + echo "============================\n"; + foreach ($it as $key=>$value) { + var_dump($key); + var_dump($value); + } + echo "============================\n\n"; +} + +?> +===DONE=== + +--EXPECTF-- +IT #0 +============================ +string(5) "key10" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key0" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key1" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key2" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key3" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key4" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key5" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key6" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key7" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key8" +array(1) { + ["type"]=> + string(4) "user" +} +string(4) "key9" +array(1) { + ["type"]=> + string(4) "user" +} +============================ + +IT #1 +============================ +string(5) "key10" +array(1) { + ["key"]=> + string(5) "key10" +} +string(4) "key0" +array(1) { + ["key"]=> + string(4) "key0" +} +string(4) "key1" +array(1) { + ["key"]=> + string(4) "key1" +} +string(4) "key2" +array(1) { + ["key"]=> + string(4) "key2" +} +string(4) "key3" +array(1) { + ["key"]=> + string(4) "key3" +} +string(4) "key4" +array(1) { + ["key"]=> + string(4) "key4" +} +string(4) "key5" +array(1) { + ["key"]=> + string(4) "key5" +} +string(4) "key6" +array(1) { + ["key"]=> + string(4) "key6" +} +string(4) "key7" +array(1) { + ["key"]=> + string(4) "key7" +} +string(4) "key8" +array(1) { + ["key"]=> + string(4) "key8" +} +string(4) "key9" +array(1) { + ["key"]=> + string(4) "key9" +} +============================ + +IT #2 +============================ +string(5) "key10" +array(0) { +} +string(4) "key0" +array(0) { +} +string(4) "key1" +array(0) { +} +string(4) "key2" +array(0) { +} +string(4) "key3" +array(0) { +} +string(4) "key4" +array(0) { +} +string(4) "key5" +array(0) { +} +string(4) "key6" +array(0) { +} +string(4) "key7" +array(0) { +} +string(4) "key8" +array(0) { +} +string(4) "key9" +array(0) { +} +============================ + +IT #3 +============================ +string(5) "key10" +array(0) { +} +string(4) "key0" +array(0) { +} +string(4) "key1" +array(0) { +} +string(4) "key2" +array(0) { +} +string(4) "key3" +array(0) { +} +string(4) "key4" +array(0) { +} +string(4) "key5" +array(0) { +} +string(4) "key6" +array(0) { +} +string(4) "key7" +array(0) { +} +string(4) "key8" +array(0) { +} +string(4) "key9" +array(0) { +} +============================ + +IT #4 +============================ +string(5) "key10" +array(0) { +} +string(4) "key0" +array(0) { +} +string(4) "key1" +array(0) { +} +string(4) "key2" +array(0) { +} +string(4) "key3" +array(0) { +} +string(4) "key4" +array(0) { +} +string(4) "key5" +array(0) { +} +string(4) "key6" +array(0) { +} +string(4) "key7" +array(0) { +} +string(4) "key8" +array(0) { +} +string(4) "key9" +array(0) { +} +============================ + +IT #5 +============================ +string(5) "key10" +array(1) { + ["value"]=> + string(7) "value10" +} +string(4) "key0" +array(1) { + ["value"]=> + string(6) "value0" +} +string(4) "key1" +array(1) { + ["value"]=> + string(6) "value1" +} +string(4) "key2" +array(1) { + ["value"]=> + string(6) "value2" +} +string(4) "key3" +array(1) { + ["value"]=> + string(6) "value3" +} +string(4) "key4" +array(1) { + ["value"]=> + string(6) "value4" +} +string(4) "key5" +array(1) { + ["value"]=> + string(6) "value5" +} +string(4) "key6" +array(1) { + ["value"]=> + string(6) "value6" +} +string(4) "key7" +array(1) { + ["value"]=> + string(6) "value7" +} +string(4) "key8" +array(1) { + ["value"]=> + string(6) "value8" +} +string(4) "key9" +array(1) { + ["value"]=> + string(6) "value9" +} +============================ + +IT #6 +============================ +string(5) "key10" +array(0) { +} +string(4) "key0" +array(0) { +} +string(4) "key1" +array(0) { +} +string(4) "key2" +array(0) { +} +string(4) "key3" +array(0) { +} +string(4) "key4" +array(0) { +} +string(4) "key5" +array(0) { +} +string(4) "key6" +array(0) { +} +string(4) "key7" +array(0) { +} +string(4) "key8" +array(0) { +} +string(4) "key9" +array(0) { +} +============================ + +IT #7 +============================ +string(5) "key10" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key0" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key1" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key2" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key3" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key4" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key5" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key6" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key7" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key8" +array(1) { + ["num_hits"]=> + int(0) +} +string(4) "key9" +array(1) { + ["num_hits"]=> + int(0) +} +============================ + +IT #8 +============================ +string(5) "key10" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key0" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key1" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key2" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key3" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key4" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key5" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key6" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key7" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key8" +array(1) { + ["mtime"]=> + int(%d) +} +string(4) "key9" +array(1) { + ["mtime"]=> + int(%d) +} +============================ + +IT #9 +============================ +string(5) "key10" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key0" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key1" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key2" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key3" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key4" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key5" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key6" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key7" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key8" +array(1) { + ["creation_time"]=> + int(%d) +} +string(4) "key9" +array(1) { + ["creation_time"]=> + int(%d) +} +============================ + +IT #10 +============================ +string(5) "key10" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key0" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key1" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key2" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key3" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key4" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key5" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key6" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key7" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key8" +array(1) { + ["deletion_time"]=> + int(0) +} +string(4) "key9" +array(1) { + ["deletion_time"]=> + int(0) +} +============================ + +IT #11 +============================ +string(5) "key10" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key0" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key1" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key2" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key3" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key4" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key5" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key6" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key7" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key8" +array(1) { + ["access_time"]=> + int(%d) +} +string(4) "key9" +array(1) { + ["access_time"]=> + int(%d) +} +============================ + +IT #12 +============================ +string(5) "key10" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key0" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key1" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key2" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key3" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key4" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key5" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key6" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key7" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key8" +array(1) { + ["ref_count"]=> + int(0) +} +string(4) "key9" +array(1) { + ["ref_count"]=> + int(0) +} +============================ + +IT #13 +============================ +string(5) "key10" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key0" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key1" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key2" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key3" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key4" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key5" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key6" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key7" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key8" +array(1) { + ["mem_size"]=> + int(%d) +} +string(4) "key9" +array(1) { + ["mem_size"]=> + int(%d) +} +============================ + +IT #14 +============================ +string(5) "key10" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key0" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key1" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key2" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key3" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key4" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key5" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key6" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key7" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key8" +array(1) { + ["ttl"]=> + int(0) +} +string(4) "key9" +array(1) { + ["ttl"]=> + int(0) +} +============================ + +IT #15 +============================ +string(5) "key10" +array(0) { +} +string(4) "key0" +array(0) { +} +string(4) "key1" +array(0) { +} +string(4) "key2" +array(0) { +} +string(4) "key3" +array(0) { +} +string(4) "key4" +array(0) { +} +string(4) "key5" +array(0) { +} +string(4) "key6" +array(0) { +} +string(4) "key7" +array(0) { +} +string(4) "key8" +array(0) { +} +string(4) "key9" +array(0) { +} +============================ + +IT #16 +============================ +string(5) "key10" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(5) "key10" + ["value"]=> + string(7) "value10" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key0" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key0" + ["value"]=> + string(6) "value0" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key1" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key1" + ["value"]=> + string(6) "value1" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key2" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key2" + ["value"]=> + string(6) "value2" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key3" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key3" + ["value"]=> + string(6) "value3" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key4" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key4" + ["value"]=> + string(6) "value4" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key5" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key5" + ["value"]=> + string(6) "value5" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key6" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key6" + ["value"]=> + string(6) "value6" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key7" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key7" + ["value"]=> + string(6) "value7" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key8" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key8" + ["value"]=> + string(6) "value8" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +string(4) "key9" +array(11) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key9" + ["value"]=> + string(6) "value9" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) + ["ttl"]=> + int(0) +} +============================ + +IT #17 +============================ +string(5) "key10" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(5) "key10" + ["value"]=> + string(7) "value10" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key0" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key0" + ["value"]=> + string(6) "value0" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key1" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key1" + ["value"]=> + string(6) "value1" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key2" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key2" + ["value"]=> + string(6) "value2" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key3" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key3" + ["value"]=> + string(6) "value3" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key4" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key4" + ["value"]=> + string(6) "value4" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key5" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key5" + ["value"]=> + string(6) "value5" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key6" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key6" + ["value"]=> + string(6) "value6" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key7" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key7" + ["value"]=> + string(6) "value7" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key8" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key8" + ["value"]=> + string(6) "value8" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key9" +array(10) { + ["type"]=> + string(4) "user" + ["key"]=> + string(4) "key9" + ["value"]=> + string(6) "value9" + ["num_hits"]=> + int(0) + ["mtime"]=> + int(%d) + ["creation_time"]=> + int(%d) + ["deletion_time"]=> + int(0) + ["access_time"]=> + int(%d) + ["ref_count"]=> + int(0) + ["mem_size"]=> + int(%d) +} +============================ + +IT #18 +============================ +string(5) "key10" +array(3) { + ["key"]=> + string(5) "key10" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key0" +array(3) { + ["key"]=> + string(4) "key0" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key1" +array(3) { + ["key"]=> + string(4) "key1" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key2" +array(3) { + ["key"]=> + string(4) "key2" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key3" +array(3) { + ["key"]=> + string(4) "key3" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key4" +array(3) { + ["key"]=> + string(4) "key4" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key5" +array(3) { + ["key"]=> + string(4) "key5" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key6" +array(3) { + ["key"]=> + string(4) "key6" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key7" +array(3) { + ["key"]=> + string(4) "key7" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key8" +array(3) { + ["key"]=> + string(4) "key8" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +string(4) "key9" +array(3) { + ["key"]=> + string(4) "key9" + ["num_hits"]=> + int(0) + ["mem_size"]=> + int(%d) +} +============================ + +===DONE=== --- /dev/null +++ b/ext/apc/tests/iterator_007.phpt @@ -0,0 +1,36 @@ +--TEST-- +APC: APCIterator Overwriting the ctor +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +--FILE-- +rewind(), + $obj->current(), + $obj->key(), + $obj->next(), + $obj->valid(), + $obj->getTotalHits(), + $obj->getTotalSize(), + $obj->getTotalCount(), + apc_delete($obj) +); +?> +--EXPECTF-- +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) + --- /dev/null +++ b/ext/apc/tests/php_5_3_ns.inc @@ -0,0 +1,18 @@ + --- /dev/null +++ b/ext/apc/TODO @@ -0,0 +1,32 @@ +Known Bugs + +1. Gallery2 doesn't work with PHP5+APC. There is something wrong + with the way methods are restored in some edge case I haven't + been able to figure out yet. + To reproduce install gallery2 and click down to an individual photo. + +2. apc_store() probably needs some checks to skip trying to store + internal classes. Something along the lines of: + + if(Z_TYPE_P(val) == IS_OBJECT) { + zend_class_entry *ce = Z_OBJCE_P(val); + if(ce->type == ZEND_INTERNAL_CLASS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot cache internal objects"); + RETURN_FALSE; + } + } + + in the apc_store() function in php_apc.c but I am wondering if it needs to do more + than that. + +Windows + +1. The following configurations (build arguments) have not been implemented yet + + (*) --enable-apc-mmap Memory mapping support + (*) --enable-apc-sem Semaphore locking support (FCNTL replacement) + (*) --enable-apc-phreadmutex Thread mutexes, while implemented we should probably rename the internals to thread + +2. Non-blocking locks is not supported either + +3. Update fileinfo to support stat info in a more portable way (see PECL #17903) \ No newline at end of file