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