r5:
[php5-apc.git] / php_apc.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
16   |          Rasmus Lerdorf <rasmus@php.net>                             |
17   +----------------------------------------------------------------------+
18
19    This software was contributed to PHP by Community Connect Inc. in 2002
20    and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
21    Future revisions and derivatives of this source code must acknowledge
22    Community Connect Inc. as the original contributor of this module by
23    leaving this note intact in the source code.
24
25    All other licensing and usage conditions are those of the PHP Group.
26
27  */
28
29 /* $Id: php_apc.c,v 3.154 2007/12/26 22:31:20 rasmus Exp $ */
30
31 #include "apc_zend.h"
32 #include "apc_cache.h"
33 #include "apc_main.h"
34 #include "apc_sma.h"
35 #include "apc_lock.h"
36 #include "php_globals.h"
37 #include "php_ini.h"
38 #include "ext/standard/info.h"
39 #include "SAPI.h"
40 #include "rfc1867.h"
41 #include "php_apc.h"
42 #if PHP_API_VERSION <= 20020918
43 #if HAVE_APACHE
44 #ifdef APC_PHP4_STAT
45 #undef XtOffsetOf
46 #include "httpd.h"
47 #endif
48 #endif
49 #endif
50
51 #if HAVE_SIGACTION
52 #include "apc_signal.h"
53 #endif
54
55 /* {{{ PHP_FUNCTION declarations */
56 PHP_FUNCTION(apc_cache_info);
57 PHP_FUNCTION(apc_clear_cache);
58 PHP_FUNCTION(apc_sma_info);
59 PHP_FUNCTION(apc_store);
60 PHP_FUNCTION(apc_fetch);
61 PHP_FUNCTION(apc_delete);
62 PHP_FUNCTION(apc_compile_file);
63 PHP_FUNCTION(apc_define_constants);
64 PHP_FUNCTION(apc_load_constants);
65 PHP_FUNCTION(apc_add);
66 /* }}} */
67
68 /* {{{ ZEND_DECLARE_MODULE_GLOBALS(apc) */
69 ZEND_DECLARE_MODULE_GLOBALS(apc)
70
71 /* True globals */
72 apc_cache_t* apc_cache = NULL;       
73 apc_cache_t* apc_user_cache = NULL;
74 void* apc_compiled_filters = NULL;
75
76 static void php_apc_init_globals(zend_apc_globals* apc_globals TSRMLS_DC)
77 {
78     apc_globals->filters = NULL;
79     apc_globals->initialized = 0;
80     apc_globals->cache_stack = apc_stack_create(0);
81     apc_globals->cache_by_default = 1;
82     apc_globals->slam_defense = 0;
83     apc_globals->mem_size_ptr = NULL;
84     apc_globals->fpstat = 1;
85     apc_globals->stat_ctime = 0;
86     apc_globals->write_lock = 1;
87     apc_globals->report_autofilter = 0;
88     apc_globals->apc_optimize_function = NULL;
89 #ifdef MULTIPART_EVENT_FORMDATA
90     apc_globals->rfc1867 = 0;
91 #endif
92     apc_globals->copied_zvals = NULL;
93 #ifdef ZEND_ENGINE_2
94     apc_globals->reserved_offset = -1;
95 #endif
96     apc_globals->localcache = 0;
97     apc_globals->localcache_size = 0;
98     apc_globals->lcache = NULL;
99     apc_globals->force_file_update = 0;
100     apc_globals->coredump_unmap = 0;
101 }
102
103 static void php_apc_shutdown_globals(zend_apc_globals* apc_globals TSRMLS_DC)
104 {
105     /* deallocate the ignore patterns */
106     if (apc_globals->filters != NULL) {
107         int i;
108         for (i=0; apc_globals->filters[i] != NULL; i++) {
109             apc_efree(apc_globals->filters[i]);
110         }
111         apc_efree(apc_globals->filters);
112     }
113
114     /* the stack should be empty */
115     assert(apc_stack_size(apc_globals->cache_stack) == 0); 
116
117     /* apc cleanup */
118     apc_stack_destroy(apc_globals->cache_stack);
119
120     /* the rest of the globals are cleaned up in apc_module_shutdown() */
121 }
122
123 /* }}} */
124
125 /* {{{ PHP_INI */
126
127 static PHP_INI_MH(OnUpdate_filters) /* {{{ */
128 {
129     APCG(filters) = apc_tokenize(new_value, ',');
130     return SUCCESS;
131 }
132 /* }}} */
133
134 static PHP_INI_MH(OnUpdateShmSegments) /* {{{ */
135 {
136 #if APC_MMAP
137     if(atoi(new_value)!=1) {
138         php_error_docref(NULL TSRMLS_CC, E_WARNING, "apc.shm_segments setting ignored in MMAP mode");
139     }
140     APCG(shm_segments) = 1; 
141 #else
142     APCG(shm_segments) = atoi(new_value);
143 #endif
144     return SUCCESS;
145 }
146 /* }}} */
147
148 #ifdef MULTIPART_EVENT_FORMDATA
149 static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */
150 {
151     int tmp;
152     tmp = zend_atoi(new_value, new_value_length);
153     if(tmp < 0) {
154         apc_eprint("rfc1867_freq must be greater than or equal to zero.");
155         return FAILURE;
156     }
157     if(new_value[new_value_length-1] == '%') {
158         if(tmp > 100) {
159             apc_eprint("rfc1867_freq cannot be over 100%%");
160             return FAILURE;
161         }
162         APCG(rfc1867_freq) = tmp / 100.0;
163     } else {
164         APCG(rfc1867_freq) = tmp;
165     }
166     return SUCCESS;
167 }
168 /* }}} */
169 #endif
170
171 #ifdef ZEND_ENGINE_2
172 #define OnUpdateInt OnUpdateLong
173 #endif
174
175 PHP_INI_BEGIN()
176 STD_PHP_INI_BOOLEAN("apc.enabled",      "1",    PHP_INI_SYSTEM, OnUpdateBool,              enabled,         zend_apc_globals, apc_globals)
177 STD_PHP_INI_ENTRY("apc.shm_segments",   "1",    PHP_INI_SYSTEM, OnUpdateShmSegments,       shm_segments,    zend_apc_globals, apc_globals)
178 STD_PHP_INI_ENTRY("apc.shm_size",       "30",   PHP_INI_SYSTEM, OnUpdateInt,            shm_size,        zend_apc_globals, apc_globals)
179 STD_PHP_INI_BOOLEAN("apc.include_once_override", "0", PHP_INI_SYSTEM, OnUpdateBool,     include_once,    zend_apc_globals, apc_globals)
180 STD_PHP_INI_ENTRY("apc.num_files_hint", "1000", PHP_INI_SYSTEM, OnUpdateInt,            num_files_hint,  zend_apc_globals, apc_globals)
181 STD_PHP_INI_ENTRY("apc.user_entries_hint", "4096", PHP_INI_SYSTEM, OnUpdateInt,          user_entries_hint, zend_apc_globals, apc_globals)
182 STD_PHP_INI_ENTRY("apc.gc_ttl",         "3600", PHP_INI_SYSTEM, OnUpdateInt,            gc_ttl,           zend_apc_globals, apc_globals)
183 STD_PHP_INI_ENTRY("apc.ttl",            "0",    PHP_INI_SYSTEM, OnUpdateInt,            ttl,              zend_apc_globals, apc_globals)
184 STD_PHP_INI_ENTRY("apc.user_ttl",       "0",    PHP_INI_SYSTEM, OnUpdateInt,            user_ttl,         zend_apc_globals, apc_globals)
185 #if APC_MMAP
186 STD_PHP_INI_ENTRY("apc.mmap_file_mask",  NULL,  PHP_INI_SYSTEM, OnUpdateString,         mmap_file_mask,   zend_apc_globals, apc_globals)
187 #endif
188 PHP_INI_ENTRY("apc.filters",        NULL,     PHP_INI_SYSTEM, OnUpdate_filters)
189 STD_PHP_INI_BOOLEAN("apc.cache_by_default", "1",  PHP_INI_ALL, OnUpdateBool,         cache_by_default, zend_apc_globals, apc_globals)
190 STD_PHP_INI_ENTRY("apc.slam_defense", "0",      PHP_INI_SYSTEM, OnUpdateInt,            slam_defense,     zend_apc_globals, apc_globals)
191 STD_PHP_INI_ENTRY("apc.file_update_protection", "2", PHP_INI_SYSTEM, OnUpdateInt,file_update_protection,  zend_apc_globals, apc_globals)
192 STD_PHP_INI_BOOLEAN("apc.enable_cli", "0",      PHP_INI_SYSTEM, OnUpdateBool,           enable_cli,       zend_apc_globals, apc_globals)
193 STD_PHP_INI_ENTRY("apc.max_file_size", "1M",    PHP_INI_SYSTEM, OnUpdateInt,            max_file_size,    zend_apc_globals, apc_globals)
194 STD_PHP_INI_BOOLEAN("apc.stat", "1",            PHP_INI_SYSTEM, OnUpdateBool,           fpstat,           zend_apc_globals, apc_globals)
195 STD_PHP_INI_BOOLEAN("apc.stat_ctime", "0",      PHP_INI_SYSTEM, OnUpdateBool,           stat_ctime,       zend_apc_globals, apc_globals)
196 STD_PHP_INI_BOOLEAN("apc.write_lock", "1",      PHP_INI_SYSTEM, OnUpdateBool,           write_lock,       zend_apc_globals, apc_globals)
197 STD_PHP_INI_BOOLEAN("apc.report_autofilter", "0", PHP_INI_SYSTEM, OnUpdateBool,         report_autofilter,zend_apc_globals, apc_globals)
198 #ifdef MULTIPART_EVENT_FORMDATA
199 STD_PHP_INI_BOOLEAN("apc.rfc1867", "0", PHP_INI_SYSTEM, OnUpdateBool, rfc1867, zend_apc_globals, apc_globals)
200 STD_PHP_INI_ENTRY("apc.rfc1867_prefix", "upload_", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_prefix, zend_apc_globals, apc_globals)
201 STD_PHP_INI_ENTRY("apc.rfc1867_name", "APC_UPLOAD_PROGRESS", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_name, zend_apc_globals, apc_globals)
202 STD_PHP_INI_ENTRY("apc.rfc1867_freq", "0", PHP_INI_SYSTEM, OnUpdateRfc1867Freq, rfc1867_freq, zend_apc_globals, apc_globals)
203 #endif
204 STD_PHP_INI_BOOLEAN("apc.localcache", "0", PHP_INI_SYSTEM, OnUpdateBool, localcache, zend_apc_globals, apc_globals)
205 STD_PHP_INI_ENTRY("apc.localcache.size", "512", PHP_INI_SYSTEM, OnUpdateInt, localcache_size,  zend_apc_globals, apc_globals)
206 STD_PHP_INI_BOOLEAN("apc.coredump_unmap", "0", PHP_INI_SYSTEM, OnUpdateBool, coredump_unmap, zend_apc_globals, apc_globals)
207 PHP_INI_END()
208
209 /* }}} */
210
211 /* {{{ PHP_MINFO_FUNCTION(apc) */
212 static PHP_MINFO_FUNCTION(apc)
213 {
214     php_info_print_table_start();
215     php_info_print_table_row(2, "APC Support", APCG(enabled) ? "enabled" : "disabled");
216     php_info_print_table_row(2, "Version", APC_VERSION);
217 #if APC_MMAP
218     php_info_print_table_row(2, "MMAP Support", "Enabled");
219     php_info_print_table_row(2, "MMAP File Mask", APCG(mmap_file_mask));
220 #else
221     php_info_print_table_row(2, "MMAP Support", "Disabled");
222 #endif
223 #if APC_SEM_LOCKS
224     php_info_print_table_row(2, "Locking type", "IPC Semaphore");
225 #elif APC_FUTEX_LOCKS
226     php_info_print_table_row(2, "Locking type", "Linux Futex Locks");
227 #elif APC_PTHREADMUTEX_LOCKS
228     php_info_print_table_row(2, "Locking type", "pthread mutex Locks");
229 #elif APC_SPIN_LOCKS
230     php_info_print_table_row(2, "Locking type", "spin Locks");
231 #else
232     php_info_print_table_row(2, "Locking type", "File Locks");
233 #endif
234     php_info_print_table_row(2, "Revision", "$Revision: 3.154 $");
235     php_info_print_table_row(2, "Build Date", __DATE__ " " __TIME__);
236     php_info_print_table_end();
237     DISPLAY_INI_ENTRIES();
238 }
239 /* }}} */
240
241 #ifdef MULTIPART_EVENT_FORMDATA
242 extern int apc_rfc1867_progress(unsigned int event, void *event_data, void **extra TSRMLS_DC);
243 #endif
244
245 /* {{{ PHP_MINIT_FUNCTION(apc) */
246 static PHP_MINIT_FUNCTION(apc)
247 {
248     ZEND_INIT_MODULE_GLOBALS(apc, php_apc_init_globals, php_apc_shutdown_globals);
249
250     REGISTER_INI_ENTRIES();
251
252     /* Disable APC in cli mode unless overridden by apc.enable_cli */
253     if(!APCG(enable_cli) && !strcmp(sapi_module.name, "cli")) {
254         APCG(enabled) = 0;
255     }
256
257     if (APCG(enabled)) {
258         if(APCG(initialized)) {
259             apc_process_init(module_number TSRMLS_CC);
260         } else {
261             apc_module_init(module_number TSRMLS_CC);
262             apc_zend_init(TSRMLS_C);
263             apc_process_init(module_number TSRMLS_CC);
264 #ifdef MULTIPART_EVENT_FORMDATA
265             /* File upload progress tracking */
266             if(APCG(rfc1867)) {
267                 php_rfc1867_callback = apc_rfc1867_progress;
268             }
269 #endif
270         }
271     }
272
273     return SUCCESS;
274 }
275 /* }}} */
276
277 /* {{{ PHP_MSHUTDOWN_FUNCTION(apc) */
278 static PHP_MSHUTDOWN_FUNCTION(apc)
279 {
280     if(APCG(enabled)) {
281         apc_process_shutdown(TSRMLS_C);
282         apc_zend_shutdown(TSRMLS_C);
283         apc_module_shutdown(TSRMLS_C);
284 #ifndef ZTS
285         php_apc_shutdown_globals(&apc_globals);
286 #endif
287     }
288 #ifdef ZTS
289     ts_free_id(apc_globals_id);
290 #endif
291     UNREGISTER_INI_ENTRIES();
292     return SUCCESS;
293 }
294 /* }}} */
295
296 /* {{{ PHP_RINIT_FUNCTION(apc) */
297 static PHP_RINIT_FUNCTION(apc)
298 {
299     if(APCG(enabled)) {
300         apc_request_init(TSRMLS_C);
301
302 #if HAVE_SIGACTION
303         apc_set_signals();
304 #endif
305     }
306     return SUCCESS;
307 }
308 /* }}} */
309
310 /* {{{ PHP_RSHUTDOWN_FUNCTION(apc) */
311 static PHP_RSHUTDOWN_FUNCTION(apc)
312 {
313     if(APCG(enabled)) {
314         apc_request_shutdown(TSRMLS_C);
315     }
316     return SUCCESS;
317 }
318 /* }}} */
319
320 /* {{{ proto array apc_cache_info([string type] [, bool limited]) */
321 PHP_FUNCTION(apc_cache_info)
322 {
323     apc_cache_info_t* info;
324     apc_cache_link_t* p;
325     zval* list;
326     char *cache_type;
327     int ct_len;
328     zend_bool limited=0;
329
330     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &cache_type, &ct_len, &limited) == FAILURE) {
331         return;
332     }
333
334     if(ZEND_NUM_ARGS()) {
335         if(!strcasecmp(cache_type,"user")) {
336             info = apc_cache_info(apc_user_cache, limited);
337         } else if(!strcasecmp(cache_type,"filehits")) {
338 #ifdef APC_FILEHITS
339             RETVAL_ZVAL(APCG(filehits), 1, 0);
340             return;
341 #else
342             RETURN_FALSE;
343 #endif
344         } else {
345             info = apc_cache_info(apc_cache, limited);
346         }
347     } else info = apc_cache_info(apc_cache, limited);
348
349     if(!info) {
350         php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC info available.  Perhaps APC is not enabled? Check apc.enabled in your ini file");
351         RETURN_FALSE;
352     }
353
354     array_init(return_value);
355     add_assoc_long(return_value, "num_slots", info->num_slots);
356     add_assoc_long(return_value, "ttl", info->ttl);
357     add_assoc_long(return_value, "num_hits", info->num_hits);
358     add_assoc_long(return_value, "num_misses", info->num_misses);
359     add_assoc_long(return_value, "start_time", info->start_time);
360     add_assoc_long(return_value, "expunges", info->expunges);
361     add_assoc_long(return_value, "mem_size", info->mem_size);
362     add_assoc_long(return_value, "num_entries", info->num_entries);
363     add_assoc_long(return_value, "num_inserts", info->num_inserts);
364 #ifdef MULTIPART_EVENT_FORMDATA
365     add_assoc_long(return_value, "file_upload_progress", 1);
366 #else
367     add_assoc_long(return_value, "file_upload_progress", 0);
368 #endif
369 #if APC_MMAP
370     add_assoc_stringl(return_value, "memory_type", "mmap", sizeof("mmap")-1, 1);
371 #else
372     add_assoc_stringl(return_value, "memory_type", "IPC shared", sizeof("IPC shared")-1, 1);
373 #endif
374 #if APC_SEM_LOCKS
375     add_assoc_stringl(return_value, "locking_type", "IPC semaphore", sizeof("IPC semaphore")-1, 1);
376 #elif APC_FUTEX_LOCKS
377     add_assoc_stringl(return_value, "locking_type", "Linux Futex", sizeof("Linux Futex")-1, 1);
378 #elif APC_PTHREADMUTEX_LOCKS
379     add_assoc_stringl(return_value, "locking_type", "pthread mutex", sizeof("pthread mutex")-1, 1);
380 #elif APC_SPIN_LOCKS
381     add_assoc_stringl(return_value, "locking_type", "spin", sizeof("spin")-1, 1);
382 #else
383     add_assoc_stringl(return_value, "locking_type", "file", sizeof("file")-1, 1);
384 #endif
385     if(limited) {
386         apc_cache_free_info(info);
387         return;
388     }
389     
390     ALLOC_INIT_ZVAL(list);
391     array_init(list);
392
393     for (p = info->list; p != NULL; p = p->next) {
394         zval* link;
395
396         ALLOC_INIT_ZVAL(link);
397         array_init(link);
398
399         if(p->type == APC_CACHE_ENTRY_FILE) {
400             add_assoc_string(link, "filename", p->data.file.filename, 1);
401             add_assoc_long(link, "device", p->data.file.device);
402             add_assoc_long(link, "inode", p->data.file.inode);
403             add_assoc_string(link, "type", "file", 1);
404         } else if(p->type == APC_CACHE_ENTRY_USER) {
405             add_assoc_string(link, "info", p->data.user.info, 1);
406             add_assoc_long(link, "ttl", (long)p->data.user.ttl);
407             add_assoc_string(link, "type", "user", 1);
408         }
409         add_assoc_long(link, "num_hits", p->num_hits);
410         add_assoc_long(link, "mtime", p->mtime);
411         add_assoc_long(link, "creation_time", p->creation_time);
412         add_assoc_long(link, "deletion_time", p->deletion_time);
413         add_assoc_long(link, "access_time", p->access_time);
414         add_assoc_long(link, "ref_count", p->ref_count);
415         add_assoc_long(link, "mem_size", p->mem_size);
416         add_next_index_zval(list, link);
417     }
418     add_assoc_zval(return_value, "cache_list", list);
419
420     ALLOC_INIT_ZVAL(list);
421     array_init(list);
422
423     for (p = info->deleted_list; p != NULL; p = p->next) {
424         zval* link;
425
426         ALLOC_INIT_ZVAL(link);
427         array_init(link);
428
429         if(p->type == APC_CACHE_ENTRY_FILE) {
430             add_assoc_string(link, "filename", p->data.file.filename, 1);
431             add_assoc_long(link, "device", p->data.file.device);
432             add_assoc_long(link, "inode", p->data.file.inode);
433             add_assoc_string(link, "type", "file", 1);
434         } else if(p->type == APC_CACHE_ENTRY_USER) {
435             add_assoc_string(link, "info", p->data.user.info, 1);
436             add_assoc_long(link, "ttl", (long)p->data.user.ttl);
437             add_assoc_string(link, "type", "user", 1);
438         }
439         add_assoc_long(link, "num_hits", p->num_hits);
440         add_assoc_long(link, "mtime", p->mtime);
441         add_assoc_long(link, "creation_time", p->creation_time);
442         add_assoc_long(link, "deletion_time", p->deletion_time);
443         add_assoc_long(link, "access_time", p->access_time);
444         add_assoc_long(link, "ref_count", p->ref_count);
445         add_assoc_long(link, "mem_size", p->mem_size);
446         add_next_index_zval(list, link);
447     }
448     add_assoc_zval(return_value, "deleted_list", list);
449
450     apc_cache_free_info(info);
451 }
452 /* }}} */
453
454 /* {{{ proto void apc_clear_cache() */
455 PHP_FUNCTION(apc_clear_cache)
456 {
457     char *cache_type;
458     int ct_len;
459
460     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache_type, &ct_len) == FAILURE) {
461         return;
462     }
463
464     if(ZEND_NUM_ARGS()) {
465         if(!strcasecmp(cache_type,"user")) {
466             apc_cache_clear(apc_user_cache);
467             RETURN_TRUE;
468         }
469     }
470     apc_cache_clear(apc_cache);
471 }
472 /* }}} */
473
474 /* {{{ proto array apc_sma_info([bool limited]) */
475 PHP_FUNCTION(apc_sma_info)
476 {
477     apc_sma_info_t* info;
478     zval* block_lists;
479     int i;
480     zend_bool limited = 0;
481
482     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &limited) == FAILURE) {
483         return;
484     }
485
486     info = apc_sma_info(limited);
487
488     if(!info) {
489         php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC SMA info available.  Perhaps APC is disabled via apc.enabled?");
490         RETURN_FALSE;
491     }
492
493     array_init(return_value);
494     add_assoc_long(return_value, "num_seg", info->num_seg);
495     add_assoc_long(return_value, "seg_size", info->seg_size);
496     add_assoc_long(return_value, "avail_mem", apc_sma_get_avail_mem());
497
498     if(limited) {
499         apc_sma_free_info(info);
500         return;
501     }
502
503 #if ALLOC_DISTRIBUTION
504     {
505         size_t *adist = apc_sma_get_alloc_distribution();
506         zval* list;
507         ALLOC_INIT_ZVAL(list);
508         array_init(list);
509         for(i=0; i<30; i++) {
510             add_next_index_long(list, adist[i]);
511         }
512         add_assoc_zval(return_value, "adist", list);
513     }
514 #endif
515     ALLOC_INIT_ZVAL(block_lists);
516     array_init(block_lists);
517
518     for (i = 0; i < info->num_seg; i++) {
519         apc_sma_link_t* p;
520         zval* list;
521
522         ALLOC_INIT_ZVAL(list);
523         array_init(list);
524
525         for (p = info->list[i]; p != NULL; p = p->next) {
526             zval* link;
527
528             ALLOC_INIT_ZVAL(link);
529             array_init(link);
530
531             add_assoc_long(link, "size", p->size);
532             add_assoc_long(link, "offset", p->offset);
533             add_next_index_zval(list, link);
534         }
535         add_next_index_zval(block_lists, list);
536     }
537     add_assoc_zval(return_value, "block_lists", block_lists);
538     apc_sma_free_info(info);
539 }
540 /* }}} */
541
542 /* {{{ _apc_store */
543 int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int ttl, const int exclusive TSRMLS_DC) {
544     apc_cache_entry_t *entry;
545     apc_cache_key_t key;
546     time_t t;
547     size_t mem_size = 0;
548
549 #if PHP_API_VERSION < 20041225
550 #if HAVE_APACHE && defined(APC_PHP4_STAT)
551     t = ((request_rec *)SG(server_context))->request_time;
552 #else
553     t = time(0);
554 #endif
555 #else
556     t = sapi_get_request_time(TSRMLS_C);
557 #endif
558
559     if(!APCG(enabled)) return 0;
560
561     HANDLE_BLOCK_INTERRUPTIONS();
562
563     APCG(mem_size_ptr) = &mem_size;
564     if (!(entry = apc_cache_make_user_entry(strkey, strkey_len + 1, val, ttl))) {
565         APCG(mem_size_ptr) = NULL;
566         apc_cache_expunge(apc_cache,t);
567         apc_cache_expunge(apc_user_cache,t);
568         HANDLE_UNBLOCK_INTERRUPTIONS();
569         return 0;
570     }
571
572     if (!apc_cache_make_user_key(&key, strkey, strkey_len + 1, t)) {
573         APCG(mem_size_ptr) = NULL;
574         apc_cache_free_entry(entry);
575         apc_cache_expunge(apc_cache,t);
576         apc_cache_expunge(apc_user_cache,t);
577         HANDLE_UNBLOCK_INTERRUPTIONS();
578         return 0;
579     }
580
581     if (!apc_cache_user_insert(apc_user_cache, key, entry, t, exclusive TSRMLS_CC)) {
582         APCG(mem_size_ptr) = NULL;
583         apc_cache_free_entry(entry);
584         apc_cache_expunge(apc_cache,t);
585         apc_cache_expunge(apc_user_cache,t);
586         HANDLE_UNBLOCK_INTERRUPTIONS();
587         return 0;
588     }
589
590     APCG(mem_size_ptr) = NULL;
591
592     HANDLE_UNBLOCK_INTERRUPTIONS();
593
594     return 1;
595 }
596 /* }}} */
597
598 /* {{{ proto int apc_store(string key, zval var [, ttl ])
599  */
600 PHP_FUNCTION(apc_store) {
601     zval *val;
602     char *strkey;
603     int strkey_len;
604     long ttl = 0L;
605
606     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) {
607         return;
608     }
609
610     if(!strkey_len) RETURN_FALSE;
611
612     if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 0 TSRMLS_CC)) RETURN_TRUE;
613     RETURN_FALSE;
614 }
615 /* }}} */
616
617 /* {{{ proto int apc_add(string key, zval var [, ttl ])
618  */
619 PHP_FUNCTION(apc_add) {
620     zval *val;
621     char *strkey;
622     int strkey_len;
623     long ttl = 0L;
624
625     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) {
626         return;
627     }
628
629     if(!strkey_len) RETURN_FALSE;
630
631     if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 1 TSRMLS_CC)) RETURN_TRUE;
632     RETURN_FALSE;
633 }
634 /* }}} */
635
636 void *apc_erealloc_wrapper(void *ptr, size_t size) {
637     return _erealloc(ptr, size, 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
638 }
639
640 /* {{{ RETURN_ZVAL for php4 */
641 #if !defined(ZEND_ENGINE_2) && !defined(RETURN_ZVAL)
642 #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } 
643 #define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
644 #define ZVAL_ZVAL(z, zv, copy, dtor) {  \
645         int is_ref, refcount;           \
646         is_ref = (z)->is_ref;           \
647         refcount = (z)->refcount;       \
648         *(z) = *(zv);                   \
649         if (copy) {                     \
650             zval_copy_ctor(z);          \
651         }                               \
652         if (dtor) {                     \
653             if (!copy) {                \
654                 ZVAL_NULL(zv);          \
655             }                           \
656             zval_ptr_dtor(&zv);         \
657         }                               \
658         (z)->is_ref = is_ref;           \
659         (z)->refcount = refcount;       \
660     }
661 #endif
662 /* }}} */
663
664 /* {{{ proto mixed apc_fetch(mixed key)
665  */
666 PHP_FUNCTION(apc_fetch) {
667     zval *key;
668     HashTable *hash;
669     HashPosition hpos;
670     zval **hentry;
671     zval *result;
672     zval *result_entry;
673     char *strkey;
674     int strkey_len;
675     apc_cache_entry_t* entry;
676     time_t t;
677
678     if(!APCG(enabled)) RETURN_FALSE;
679
680     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) == FAILURE) {
681         return;
682     }
683
684 #if PHP_API_VERSION < 20041225
685 #if HAVE_APACHE && defined(APC_PHP4_STAT)
686     t = ((request_rec *)SG(server_context))->request_time;
687 #else 
688     t = time(0);
689 #endif
690 #else
691     t = sapi_get_request_time(TSRMLS_C);
692 #endif
693
694     if(Z_TYPE_P(key) != IS_STRING && Z_TYPE_P(key) != IS_ARRAY) {
695         convert_to_string(key);
696     }
697     
698     if(Z_TYPE_P(key) == IS_STRING) {
699         strkey = Z_STRVAL_P(key);
700         strkey_len = Z_STRLEN_P(key);
701         if(!strkey_len) RETURN_FALSE;
702         entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
703         if(entry) {
704             /* deep-copy returned shm zval to emalloc'ed return_value */
705             apc_cache_fetch_zval(return_value, entry->data.user.val, apc_php_malloc, apc_php_free);
706             apc_cache_release(apc_user_cache, entry);
707         } else {
708             RETURN_FALSE;
709         }
710     } else if(Z_TYPE_P(key) == IS_ARRAY) {
711         hash = Z_ARRVAL_P(key);
712         MAKE_STD_ZVAL(result);
713         array_init(result); 
714         zend_hash_internal_pointer_reset_ex(hash, &hpos);
715         while(zend_hash_get_current_data_ex(hash, (void**)&hentry, &hpos) == SUCCESS) {
716             if(Z_TYPE_PP(hentry) != IS_STRING) {
717                 apc_wprint("apc_fetch() expects a string or array of strings.");
718                 RETURN_FALSE;
719             }
720             entry = apc_cache_user_find(apc_user_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1, t);
721             if(entry) {
722                 /* deep-copy returned shm zval to emalloc'ed return_value */
723                 MAKE_STD_ZVAL(result_entry);
724                 apc_cache_fetch_zval(result_entry, entry->data.user.val, apc_php_malloc, apc_php_free);
725                 apc_cache_release(apc_user_cache, entry);
726                 zend_hash_add(Z_ARRVAL_P(result), Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) +1, &result_entry, sizeof(zval*), NULL);
727             } /* don't set values we didn't find */
728             zend_hash_move_forward_ex(hash, &hpos);
729         }
730         RETURN_ZVAL(result, 0, 1);
731     } else {
732         apc_wprint("apc_fetch() expects a string or array of strings.");
733         RETURN_FALSE;
734     }
735
736     return;
737 }
738 /* }}} */
739
740 /* {{{ proto mixed apc_delete(string key)
741  */
742 PHP_FUNCTION(apc_delete) {
743     char *strkey;
744     int strkey_len;
745
746     if(!APCG(enabled)) RETURN_FALSE;
747
748     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) {
749         return;
750     }
751
752     if(!strkey_len) RETURN_FALSE;
753
754     if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) {
755         RETURN_TRUE;
756     } else {
757         RETURN_FALSE;
758     }
759 }
760 /* }}} */
761
762 static void _apc_define_constants(zval *constants, zend_bool case_sensitive TSRMLS_DC) {
763     char *const_key;
764     unsigned int const_key_len;
765     zval **entry;
766     HashPosition pos;
767
768     zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(constants), &pos);
769     while (zend_hash_get_current_data_ex(Z_ARRVAL_P(constants), (void**)&entry, &pos) == SUCCESS) {
770         zend_constant c;
771         int key_type;
772         ulong num_key;
773
774         key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(constants), &const_key, &const_key_len, &num_key, 0, &pos);
775         if(key_type != HASH_KEY_IS_STRING) {
776             zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
777             continue;
778         }
779         switch(Z_TYPE_PP(entry)) {
780             case IS_LONG:
781             case IS_DOUBLE:
782             case IS_STRING:
783             case IS_BOOL:
784             case IS_RESOURCE:
785             case IS_NULL:
786                 break;
787             default:
788                 zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
789                 continue;
790         }
791         c.value = **entry;
792         zval_copy_ctor(&c.value);
793         c.flags = case_sensitive;
794         c.name = zend_strndup(const_key, const_key_len);
795         c.name_len = const_key_len;
796 #ifdef ZEND_ENGINE_2
797         c.module_number = PHP_USER_CONSTANT;
798 #endif
799         zend_register_constant(&c TSRMLS_CC);
800
801         zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
802     }
803 }
804
805 /* {{{ proto mixed apc_define_constants(string key, array constants [,bool case-sensitive])
806  */
807 PHP_FUNCTION(apc_define_constants) {
808     char *strkey;
809     int strkey_len;
810     zval *constants = NULL;
811     zend_bool case_sensitive = 1;
812     int argc = ZEND_NUM_ARGS();
813
814     if (zend_parse_parameters(argc TSRMLS_CC, "sa|b", &strkey, &strkey_len, &constants, &case_sensitive) == FAILURE) {
815         return;
816     }
817
818     if(!strkey_len) RETURN_FALSE;
819
820     _apc_define_constants(constants, case_sensitive TSRMLS_CC);
821     if(_apc_store(strkey, strkey_len, constants, 0, 0 TSRMLS_CC)) RETURN_TRUE;
822     RETURN_FALSE;
823 } /* }}} */
824
825 /* {{{ proto mixed apc_load_constants(string key [, bool case-sensitive])
826  */
827 PHP_FUNCTION(apc_load_constants) {
828     char *strkey;
829     int strkey_len;
830     apc_cache_entry_t* entry;
831     time_t t;
832     zend_bool case_sensitive = 1;
833
834     if(!APCG(enabled)) RETURN_FALSE;
835     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &strkey, &strkey_len, &case_sensitive) == FAILURE) {
836         return;
837     }
838
839     if(!strkey_len) RETURN_FALSE;
840
841 #if PHP_API_VERSION < 20041225
842 #if HAVE_APACHE && defined(APC_PHP4_STAT)
843     t = ((request_rec *)SG(server_context))->request_time;
844 #else 
845     t = time(0);
846 #endif
847 #else 
848     t = sapi_get_request_time(TSRMLS_C);
849 #endif
850
851     entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
852
853     if(entry) {
854         _apc_define_constants(entry->data.user.val, case_sensitive TSRMLS_CC);
855         apc_cache_release(apc_user_cache, entry);
856         RETURN_TRUE;
857     } else {
858         RETURN_FALSE;
859     }
860 }
861 /* }}} */
862
863 /* {{{ proto boolean apc_compile_file(string filename)
864  */
865 PHP_FUNCTION(apc_compile_file) {
866     char *filename;
867     int filename_len;
868     zend_file_handle file_handle;
869     zend_op_array *op_array;
870     long slam_defense = 0;
871     char** filters = NULL;
872     zend_bool cache_by_default = 1;
873     HashTable cg_function_table, cg_class_table, eg_function_table, eg_class_table;
874     HashTable *cg_orig_function_table, *cg_orig_class_table, *eg_orig_function_table, *eg_orig_class_table;
875
876     if(!APCG(enabled)) RETURN_FALSE;
877
878     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
879         return;
880     }
881
882     if(!filename) RETURN_FALSE;
883
884     /* reset slam defense, filters, and cache_by_default */
885     slam_defense = APCG(slam_defense);
886     APCG(slam_defense) = 0;
887    
888     filters = APCG(filters);
889     APCG(filters) = NULL;
890
891     cache_by_default = APCG(cache_by_default);
892     APCG(cache_by_default) = 1;
893
894     /* Replace function/class tables to avoid namespace conflicts */
895     zend_hash_init_ex(&cg_function_table, 100, NULL, ZEND_FUNCTION_DTOR, 1, 0);
896     cg_orig_function_table = CG(function_table);
897     CG(function_table) = &cg_function_table;
898     zend_hash_init_ex(&cg_class_table, 10, NULL, ZEND_CLASS_DTOR, 1, 0);
899     cg_orig_class_table = CG(class_table);
900     CG(class_table) = &cg_class_table;
901     eg_orig_function_table = EG(function_table);
902     EG(function_table) = CG(function_table);
903     eg_orig_class_table = EG(class_table);
904     EG(class_table) = CG(class_table);
905     APCG(force_file_update) = 1;
906     
907     /* Compile the file, loading it into the cache */
908     file_handle.type = ZEND_HANDLE_FILENAME;
909     file_handle.filename = filename;
910     file_handle.free_filename = 0;
911     file_handle.opened_path = NULL;
912     zend_try {
913         op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
914     } zend_catch {
915         apc_wprint("Error compiling %s in apc_compile_file.", filename);
916         op_array = NULL;
917     } zend_end_try();
918
919     /* Return class/function tables to previous states, destroy temp tables */
920     APCG(force_file_update) = 0;
921     CG(function_table) = cg_orig_function_table;
922     zend_hash_destroy(&cg_function_table);
923     CG(class_table) = cg_orig_class_table;
924     zend_hash_destroy(&cg_class_table);
925     EG(function_table) = eg_orig_function_table;
926     EG(class_table) = eg_orig_class_table;
927     
928     /* Restore global settings */
929     APCG(slam_defense) = slam_defense;
930     APCG(filters) = filters;
931     APCG(cache_by_default) = cache_by_default;
932
933     if(op_array == NULL) { RETURN_FALSE; }
934
935     /* Free up everything */
936     zend_destroy_file_handle(&file_handle TSRMLS_CC);
937 #ifdef ZEND_ENGINE_2
938     destroy_op_array(op_array TSRMLS_CC);
939 #else
940     destroy_op_array(op_array);
941 #endif
942     efree(op_array);
943
944     RETURN_TRUE;
945 }
946 /* }}} */
947
948 /* {{{ apc_functions[] */
949 function_entry apc_functions[] = {
950         PHP_FE(apc_cache_info,          NULL)
951         PHP_FE(apc_clear_cache,         NULL)
952         PHP_FE(apc_sma_info,            NULL)
953         PHP_FE(apc_store,               NULL)
954         PHP_FE(apc_fetch,               NULL)
955         PHP_FE(apc_delete,              NULL)
956         PHP_FE(apc_define_constants,    NULL)
957         PHP_FE(apc_load_constants,      NULL)
958         PHP_FE(apc_compile_file,        NULL)
959         PHP_FE(apc_add,                 NULL)
960         {NULL,          NULL,                           NULL}
961 };
962 /* }}} */
963
964 /* {{{ module definition structure */
965
966 zend_module_entry apc_module_entry = {
967         STANDARD_MODULE_HEADER,
968         "apc",
969         apc_functions,
970         PHP_MINIT(apc),
971         PHP_MSHUTDOWN(apc),
972         PHP_RINIT(apc),
973         PHP_RSHUTDOWN(apc),
974         PHP_MINFO(apc),
975         APC_VERSION,
976         STANDARD_MODULE_PROPERTIES
977 };
978
979 #ifdef COMPILE_DL_APC
980 ZEND_GET_MODULE(apc)
981 #endif
982 /* }}} */
983
984 /*
985  * Local variables:
986  * tab-width: 4
987  * c-basic-offset: 4
988  * End:
989  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
990  * vim<600: expandtab sw=4 ts=4 sts=4
991  */