New PHP5 APC - version 3.0.19, using PHP5 5.2.0-8+etch11,
[php5-apc.git] / php_apc.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 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.5 2008/05/11 18:57:00 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->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.5 $");
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     if (!(entry = apc_cache_make_user_entry(strkey, strkey_len + 1, val, ttl))) {
563         APCG(mem_size_ptr) = NULL;
564         apc_cache_expunge(apc_cache,t);
565         apc_cache_expunge(apc_user_cache,t);
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         apc_cache_free_entry(entry);
573         /* make_user_key doesn't allocate anything, so no expunge */
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         /* 
582            No expunge here - user_insert can fail for non-memory related reasons
583            The alloc-based expunge hook in 3.1 will do a better job here
584            (removing the expunge here also fixes bug #13336)
585         */
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, mixed var [, long 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, mixed var [, long 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 /* {{{ RETURN_ZVAL for php4 */
640 #if !defined(ZEND_ENGINE_2) && !defined(RETURN_ZVAL)
641 #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } 
642 #define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
643 #define ZVAL_ZVAL(z, zv, copy, dtor) {  \
644         int is_ref, refcount;           \
645         is_ref = (z)->is_ref;           \
646         refcount = (z)->refcount;       \
647         *(z) = *(zv);                   \
648         if (copy) {                     \
649             zval_copy_ctor(z);          \
650         }                               \
651         if (dtor) {                     \
652             if (!copy) {                \
653                 ZVAL_NULL(zv);          \
654             }                           \
655             zval_ptr_dtor(&zv);         \
656         }                               \
657         (z)->is_ref = is_ref;           \
658         (z)->refcount = refcount;       \
659     }
660 #endif
661 /* }}} */
662
663 /* {{{ proto mixed apc_fetch(mixed key[, bool &success])
664  */
665 PHP_FUNCTION(apc_fetch) {
666     zval *key;
667     zval *success = NULL;
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|z", &key, &success) == 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 (success) {
695         ZVAL_BOOL(success, 0);
696     }
697
698     if(Z_TYPE_P(key) != IS_STRING && Z_TYPE_P(key) != IS_ARRAY) {
699         convert_to_string(key);
700     }
701     
702     if(Z_TYPE_P(key) == IS_STRING) {
703         strkey = Z_STRVAL_P(key);
704         strkey_len = Z_STRLEN_P(key);
705         if(!strkey_len) RETURN_FALSE;
706         entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
707         if(entry) {
708             /* deep-copy returned shm zval to emalloc'ed return_value */
709             apc_cache_fetch_zval(return_value, entry->data.user.val, apc_php_malloc, apc_php_free);
710             apc_cache_release(apc_user_cache, entry);
711         } else {
712             RETURN_FALSE;
713         }
714     } else if(Z_TYPE_P(key) == IS_ARRAY) {
715         hash = Z_ARRVAL_P(key);
716         MAKE_STD_ZVAL(result);
717         array_init(result); 
718         zend_hash_internal_pointer_reset_ex(hash, &hpos);
719         while(zend_hash_get_current_data_ex(hash, (void**)&hentry, &hpos) == SUCCESS) {
720             if(Z_TYPE_PP(hentry) != IS_STRING) {
721                 apc_wprint("apc_fetch() expects a string or array of strings.");
722                 RETURN_FALSE;
723             }
724             entry = apc_cache_user_find(apc_user_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1, t);
725             if(entry) {
726                 /* deep-copy returned shm zval to emalloc'ed return_value */
727                 MAKE_STD_ZVAL(result_entry);
728                 apc_cache_fetch_zval(result_entry, entry->data.user.val, apc_php_malloc, apc_php_free);
729                 apc_cache_release(apc_user_cache, entry);
730                 zend_hash_add(Z_ARRVAL_P(result), Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) +1, &result_entry, sizeof(zval*), NULL);
731             } /* don't set values we didn't find */
732             zend_hash_move_forward_ex(hash, &hpos);
733         }
734         RETVAL_ZVAL(result, 0, 1);
735     } else {
736         apc_wprint("apc_fetch() expects a string or array of strings.");
737         RETURN_FALSE;
738     }
739
740     if (success) {
741         ZVAL_BOOL(success, 1);
742     }
743
744     return;
745 }
746 /* }}} */
747
748 /* {{{ proto mixed apc_delete(string key)
749  */
750 PHP_FUNCTION(apc_delete) {
751     char *strkey;
752     int strkey_len;
753
754     if(!APCG(enabled)) RETURN_FALSE;
755
756     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) {
757         return;
758     }
759
760     if(!strkey_len) RETURN_FALSE;
761
762     if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) {
763         RETURN_TRUE;
764     } else {
765         RETURN_FALSE;
766     }
767 }
768 /* }}} */
769
770 static void _apc_define_constants(zval *constants, zend_bool case_sensitive TSRMLS_DC) {
771     char *const_key;
772     unsigned int const_key_len;
773     zval **entry;
774     HashPosition pos;
775
776     zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(constants), &pos);
777     while (zend_hash_get_current_data_ex(Z_ARRVAL_P(constants), (void**)&entry, &pos) == SUCCESS) {
778         zend_constant c;
779         int key_type;
780         ulong num_key;
781
782         key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(constants), &const_key, &const_key_len, &num_key, 0, &pos);
783         if(key_type != HASH_KEY_IS_STRING) {
784             zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
785             continue;
786         }
787         switch(Z_TYPE_PP(entry)) {
788             case IS_LONG:
789             case IS_DOUBLE:
790             case IS_STRING:
791             case IS_BOOL:
792             case IS_RESOURCE:
793             case IS_NULL:
794                 break;
795             default:
796                 zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
797                 continue;
798         }
799         c.value = **entry;
800         zval_copy_ctor(&c.value);
801         c.flags = case_sensitive;
802         c.name = zend_strndup(const_key, const_key_len);
803         c.name_len = const_key_len;
804 #ifdef ZEND_ENGINE_2
805         c.module_number = PHP_USER_CONSTANT;
806 #endif
807         zend_register_constant(&c TSRMLS_CC);
808
809         zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos);
810     }
811 }
812
813 /* {{{ proto mixed apc_define_constants(string key, array constants [,bool case-sensitive])
814  */
815 PHP_FUNCTION(apc_define_constants) {
816     char *strkey;
817     int strkey_len;
818     zval *constants = NULL;
819     zend_bool case_sensitive = 1;
820     int argc = ZEND_NUM_ARGS();
821
822     if (zend_parse_parameters(argc TSRMLS_CC, "sa|b", &strkey, &strkey_len, &constants, &case_sensitive) == FAILURE) {
823         return;
824     }
825
826     if(!strkey_len) RETURN_FALSE;
827
828     _apc_define_constants(constants, case_sensitive TSRMLS_CC);
829     if(_apc_store(strkey, strkey_len, constants, 0, 0 TSRMLS_CC)) RETURN_TRUE;
830     RETURN_FALSE;
831 } /* }}} */
832
833 /* {{{ proto mixed apc_load_constants(string key [, bool case-sensitive])
834  */
835 PHP_FUNCTION(apc_load_constants) {
836     char *strkey;
837     int strkey_len;
838     apc_cache_entry_t* entry;
839     time_t t;
840     zend_bool case_sensitive = 1;
841
842     if(!APCG(enabled)) RETURN_FALSE;
843     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &strkey, &strkey_len, &case_sensitive) == FAILURE) {
844         return;
845     }
846
847     if(!strkey_len) RETURN_FALSE;
848
849 #if PHP_API_VERSION < 20041225
850 #if HAVE_APACHE && defined(APC_PHP4_STAT)
851     t = ((request_rec *)SG(server_context))->request_time;
852 #else 
853     t = time(0);
854 #endif
855 #else 
856     t = sapi_get_request_time(TSRMLS_C);
857 #endif
858
859     entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t);
860
861     if(entry) {
862         _apc_define_constants(entry->data.user.val, case_sensitive TSRMLS_CC);
863         apc_cache_release(apc_user_cache, entry);
864         RETURN_TRUE;
865     } else {
866         RETURN_FALSE;
867     }
868 }
869 /* }}} */
870
871 /* {{{ proto boolean apc_compile_file(string filename)
872  */
873 PHP_FUNCTION(apc_compile_file) {
874     char *filename;
875     int filename_len;
876     zend_file_handle file_handle;
877     zend_op_array *op_array;
878     long slam_defense = 0;
879     char** filters = NULL;
880     zend_bool cache_by_default = 1;
881     HashTable cg_function_table, cg_class_table, eg_function_table, eg_class_table;
882     HashTable *cg_orig_function_table, *cg_orig_class_table, *eg_orig_function_table, *eg_orig_class_table;
883
884     if(!APCG(enabled)) RETURN_FALSE;
885
886     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
887         return;
888     }
889
890     if(!filename) RETURN_FALSE;
891
892     /* reset slam defense, filters, and cache_by_default */
893     slam_defense = APCG(slam_defense);
894     APCG(slam_defense) = 0;
895    
896     filters = APCG(filters);
897     APCG(filters) = NULL;
898
899     cache_by_default = APCG(cache_by_default);
900     APCG(cache_by_default) = 1;
901
902     /* Replace function/class tables to avoid namespace conflicts */
903     zend_hash_init_ex(&cg_function_table, 100, NULL, ZEND_FUNCTION_DTOR, 1, 0);
904     cg_orig_function_table = CG(function_table);
905     CG(function_table) = &cg_function_table;
906     zend_hash_init_ex(&cg_class_table, 10, NULL, ZEND_CLASS_DTOR, 1, 0);
907     cg_orig_class_table = CG(class_table);
908     CG(class_table) = &cg_class_table;
909     eg_orig_function_table = EG(function_table);
910     EG(function_table) = CG(function_table);
911     eg_orig_class_table = EG(class_table);
912     EG(class_table) = CG(class_table);
913     APCG(force_file_update) = 1;
914     
915     /* Compile the file, loading it into the cache */
916     file_handle.type = ZEND_HANDLE_FILENAME;
917     file_handle.filename = filename;
918     file_handle.free_filename = 0;
919     file_handle.opened_path = NULL;
920     zend_try {
921         op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
922     } zend_catch {
923         apc_wprint("Error compiling %s in apc_compile_file.", filename);
924         op_array = NULL;
925     } zend_end_try();
926
927     /* Return class/function tables to previous states, destroy temp tables */
928     APCG(force_file_update) = 0;
929     CG(function_table) = cg_orig_function_table;
930     zend_hash_destroy(&cg_function_table);
931     CG(class_table) = cg_orig_class_table;
932     zend_hash_destroy(&cg_class_table);
933     EG(function_table) = eg_orig_function_table;
934     EG(class_table) = eg_orig_class_table;
935     
936     /* Restore global settings */
937     APCG(slam_defense) = slam_defense;
938     APCG(filters) = filters;
939     APCG(cache_by_default) = cache_by_default;
940
941     if(op_array == NULL) { RETURN_FALSE; }
942
943     /* Free up everything */
944     zend_destroy_file_handle(&file_handle TSRMLS_CC);
945 #ifdef ZEND_ENGINE_2
946     destroy_op_array(op_array TSRMLS_CC);
947 #else
948     destroy_op_array(op_array);
949 #endif
950     efree(op_array);
951
952     RETURN_TRUE;
953 }
954 /* }}} */
955
956 #ifdef ZEND_ENGINE_2
957 /* {{{ arginfo */
958 static
959 ZEND_BEGIN_ARG_INFO(php_apc_fetch_arginfo, 0)
960     ZEND_ARG_INFO(0, "key")
961     ZEND_ARG_INFO(1, "success")
962 ZEND_END_ARG_INFO()
963 /* }}} */
964 #else
965 #define php_apc_fetch_arginfo NULL
966 #endif
967
968
969 /* {{{ apc_functions[] */
970 function_entry apc_functions[] = {
971         PHP_FE(apc_cache_info,          NULL)
972         PHP_FE(apc_clear_cache,         NULL)
973         PHP_FE(apc_sma_info,            NULL)
974         PHP_FE(apc_store,               NULL)
975         PHP_FE(apc_fetch,               php_apc_fetch_arginfo)
976         PHP_FE(apc_delete,              NULL)
977         PHP_FE(apc_define_constants,    NULL)
978         PHP_FE(apc_load_constants,      NULL)
979         PHP_FE(apc_compile_file,        NULL)
980         PHP_FE(apc_add,                 NULL)
981         {NULL,          NULL,                           NULL}
982 };
983 /* }}} */
984
985 /* {{{ module definition structure */
986
987 zend_module_entry apc_module_entry = {
988         STANDARD_MODULE_HEADER,
989         "apc",
990         apc_functions,
991         PHP_MINIT(apc),
992         PHP_MSHUTDOWN(apc),
993         PHP_RINIT(apc),
994         PHP_RSHUTDOWN(apc),
995         PHP_MINFO(apc),
996         APC_VERSION,
997         STANDARD_MODULE_PROPERTIES
998 };
999
1000 #ifdef COMPILE_DL_APC
1001 ZEND_GET_MODULE(apc)
1002 #endif
1003 /* }}} */
1004
1005 /*
1006  * Local variables:
1007  * tab-width: 4
1008  * c-basic-offset: 4
1009  * End:
1010  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
1011  * vim<600: expandtab sw=4 ts=4 sts=4
1012  */