update php-apc to 3.0.16, disable pthread mutex's as they are on by default now.
[openwrt/svn-archive/archive.git] / lang / php5 / patches / 005-APC.patch
1 diff -ubrN php-5.2.5-orig/ext/apc/apc.c php-5.2.5/ext/apc/apc.c
2 --- php-5.2.5-orig/ext/apc/apc.c 1969-12-31 18:00:00.000000000 -0600
3 +++ php-5.2.5/ext/apc/apc.c 2007-12-26 16:51:32.000000000 -0600
4 @@ -0,0 +1,554 @@
5 +/*
6 + +----------------------------------------------------------------------+
7 + | APC |
8 + +----------------------------------------------------------------------+
9 + | Copyright (c) 2006 The PHP Group |
10 + +----------------------------------------------------------------------+
11 + | This source file is subject to version 3.01 of the PHP license, |
12 + | that is bundled with this package in the file LICENSE, and is |
13 + | available through the world-wide-web at the following url: |
14 + | http://www.php.net/license/3_01.txt |
15 + | If you did not receive a copy of the PHP license and are unable to |
16 + | obtain it through the world-wide-web, please send a note to |
17 + | license@php.net so we can mail you a copy immediately. |
18 + +----------------------------------------------------------------------+
19 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
20 + | George Schlossnagle <george@omniti.com> |
21 + | Rasmus Lerdorf <rasmus@php.net> |
22 + | Arun C. Murthy <arunc@yahoo-inc.com> |
23 + | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
24 + +----------------------------------------------------------------------+
25 +
26 + This software was contributed to PHP by Community Connect Inc. in 2002
27 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
28 + Future revisions and derivatives of this source code must acknowledge
29 + Community Connect Inc. as the original contributor of this module by
30 + leaving this note intact in the source code.
31 +
32 + All other licensing and usage conditions are those of the PHP Group.
33 +
34 + */
35 +
36 +/* $Id: apc.c,v 3.18 2007/11/29 22:15:53 shire Exp $ */
37 +
38 +#include "apc.h"
39 +#include <regex.h> /* for POSIX regular expressions */
40 +#include "php.h"
41 +
42 +#define NELEMS(a) (sizeof(a)/sizeof((a)[0]))
43 +
44 +/* {{{ memory allocation wrappers */
45 +
46 +void* apc_emalloc(size_t n)
47 +{
48 + void* p = malloc(n);
49 + if (p == NULL) {
50 + apc_eprint("apc_emalloc: malloc failed to allocate %u bytes:", n);
51 + }
52 + return p;
53 +}
54 +
55 +void* apc_erealloc(void* p, size_t n)
56 +{
57 + p = realloc(p, n);
58 + if (p == NULL) {
59 + apc_eprint("apc_erealloc: realloc failed to allocate %u bytes:", n);
60 + }
61 + return p;
62 +}
63 +
64 +void apc_efree(void* p)
65 +{
66 + if (p == NULL) {
67 + apc_eprint("apc_efree: attempt to free null pointer");
68 + }
69 + free(p);
70 +}
71 +
72 +char* apc_estrdup(const char* s)
73 +{
74 + int len;
75 + char* dup;
76 +
77 + if (s == NULL) {
78 + return NULL;
79 + }
80 + len = strlen(s);
81 + dup = (char*) malloc(len+1);
82 + if (dup == NULL) {
83 + apc_eprint("apc_estrdup: malloc failed to allocate %u bytes:", len+1);
84 + }
85 + memcpy(dup, s, len);
86 + dup[len] = '\0';
87 + return dup;
88 +}
89 +
90 +void* apc_xstrdup(const char* s, apc_malloc_t f)
91 +{
92 + return s != NULL ? apc_xmemcpy(s, strlen(s)+1, f) : NULL;
93 +}
94 +
95 +void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f)
96 +{
97 + void* q;
98 +
99 + if (p != NULL && (q = f(n)) != NULL) {
100 + memcpy(q, p, n);
101 + return q;
102 + }
103 + return NULL;
104 +}
105 +
106 +/* }}} */
107 +
108 +/* {{{ console display functions */
109 +
110 +static void my_log(int level, const char* fmt, va_list args)
111 +{
112 + static const char* level_strings[] = {
113 + "apc-debug",
114 + "apc-notice",
115 + "apc-warning",
116 + "apc-error"
117 + };
118 + static const int num_levels = NELEMS(level_strings);
119 +
120 + time_t now;
121 + char* buf; /* for ctime */
122 +
123 + fflush(stdout);
124 +
125 + if (level < 0)
126 + level = 0;
127 + else if (level >= num_levels)
128 + level = num_levels-1;
129 +
130 + now = time(0);
131 + buf = ctime(&now); /* TODO: replace with reentrant impl */
132 + buf[24] = '\0';
133 +
134 + fprintf(stderr, "[%s] [%s] ", buf, level_strings[level]);
135 + vfprintf(stderr, fmt, args);
136 +
137 + if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
138 + fprintf(stderr, " %s", strerror(errno));
139 + }
140 + fprintf(stderr, "\n");
141 +
142 + if (level == APC_ERROR) {
143 + exit(2);
144 + }
145 +}
146 +
147 +void apc_log(int level, const char* fmt, ...)
148 +{
149 + va_list args;
150 + va_start(args, fmt);
151 + my_log(level, fmt, args);
152 + va_end(args);
153 +}
154 +
155 +void apc_eprint(const char* fmt, ...)
156 +{
157 + va_list args;
158 + va_start(args, fmt);
159 + my_log(APC_ERROR, fmt, args);
160 + va_end(args);
161 +}
162 +
163 +void apc_wprint(const char* fmt, ...)
164 +{
165 + va_list args;
166 + va_start(args, fmt);
167 + my_log(APC_WARNING, fmt, args);
168 + va_end(args);
169 +}
170 +
171 +void apc_nprint(const char* fmt, ...)
172 +{
173 + va_list args;
174 + va_start(args, fmt);
175 + my_log(APC_NOTICE, fmt, args);
176 + va_end(args);
177 +}
178 +
179 +void apc_dprint(const char* fmt, ...)
180 +{
181 +#ifdef APC_DEBUG
182 + va_list args;
183 + va_start(args, fmt);
184 + my_log(APC_DBG, fmt, args);
185 + va_end(args);
186 +#endif
187 +}
188 +
189 +/* }}} */
190 +
191 +/* {{{ string and text manipulation */
192 +
193 +char* apc_append(const char* s, const char* t)
194 +{
195 + int slen;
196 + int tlen;
197 + char* p;
198 +
199 + slen = strlen(s);
200 + tlen = strlen(t);
201 +
202 + p = (char*) apc_emalloc((slen + tlen + 1) * sizeof(char));
203 + memcpy(p, s, slen);
204 + memcpy(p + slen, t, tlen + 1);
205 +
206 + return p;
207 +}
208 +
209 +char* apc_substr(const char* s, int start, int length)
210 +{
211 + char* substr;
212 + int src_len = strlen(s);
213 +
214 + /* bring start into range */
215 + if (start < 0) {
216 + start = 0;
217 + }
218 + else if (start >= src_len) {
219 + start = src_len - 1;
220 + }
221 +
222 + /* bring length into range */
223 + if (length < 0 || src_len - start < length) {
224 + length = src_len - start;
225 + }
226 +
227 + /* create the substring */
228 + substr = apc_xmemcpy(s + start, length + 1, apc_emalloc);
229 + substr[length] = '\0';
230 + return substr;
231 +}
232 +
233 +char** apc_tokenize(const char* s, char delim)
234 +{
235 + char** tokens; /* array of tokens, NULL terminated */
236 + int size; /* size of tokens array */
237 + int n; /* index of next token in tokens array */
238 + int cur; /* current position in input string */
239 + int end; /* final legal position in input string */
240 + int next; /* position of next delimiter in input */
241 +
242 + if (!s) {
243 + return NULL;
244 + }
245 +
246 + size = 2;
247 + n = 0;
248 + cur = 0;
249 + end = strlen(s) - 1;
250 +
251 + tokens = (char**) apc_emalloc(size * sizeof(char*));
252 + tokens[n] = NULL;
253 +
254 + while (cur <= end) {
255 + /* search for the next delimiter */
256 + char* p = strchr(s + cur, delim);
257 + next = p ? p-s : end+1;
258 +
259 + /* resize token array if necessary */
260 + if (n == size-1) {
261 + size *= 2;
262 + tokens = (char**) apc_erealloc(tokens, size * sizeof(char*));
263 + }
264 +
265 + /* save the current token */
266 + tokens[n] = apc_substr(s, cur, next-cur);
267 +
268 + tokens[++n] = NULL;
269 + cur = next + 1;
270 + }
271 +
272 + return tokens;
273 +}
274 +
275 +/* }}} */
276 +
277 +/* {{{ filesystem functions */
278 +
279 +#ifdef PHP_WIN32
280 +int apc_win32_stat(const char *path, struct stat *buf TSRMLS_DC)
281 +{
282 + char rpath[MAXPATHLEN];
283 + BY_HANDLE_FILE_INFORMATION fi;
284 + HANDLE f;
285 +
286 + if (VCWD_STAT(path, buf)) {
287 + return -1;
288 + }
289 +
290 + VCWD_REALPATH(path, rpath);
291 + f = CreateFile(rpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL);
292 + GetFileInformationByHandle(f, &fi);
293 + buf->st_ino = (ino_t)fi.nFileIndexLow;
294 + CloseHandle (f);
295 + return 0;
296 +}
297 +#endif
298 +
299 +int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo)
300 +{
301 + char** paths;
302 + char *exec_fname;
303 + int exec_fname_length;
304 + int found = 0;
305 + int i;
306 + TSRMLS_FETCH();
307 +
308 + assert(filename && fileinfo);
309 +
310 + if (IS_ABSOLUTE_PATH(filename, strlen(filename)) && apc_stat(filename, &fileinfo->st_buf) == 0) {
311 + strncpy(fileinfo->fullpath, filename, MAXPATHLEN);
312 + return 0;
313 + }
314 +
315 + paths = apc_tokenize(path, DEFAULT_DIR_SEPARATOR);
316 + if (!paths)
317 + return -1;
318 +
319 + /* for each directory in paths, look for filename inside */
320 + for (i = 0; paths[i]; i++) {
321 + snprintf(fileinfo->fullpath, sizeof(fileinfo->fullpath), "%s%c%s", paths[i], DEFAULT_SLASH, filename);
322 + if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) {
323 + found = 1;
324 + break;
325 + }
326 + }
327 +
328 + /* check in path of the calling scripts' current working directory */
329 + /* modified from main/streams/plain_wrapper.c */
330 + if(!found && zend_is_executing(TSRMLS_C)) {
331 + exec_fname = zend_get_executed_filename(TSRMLS_C);
332 + exec_fname_length = strlen(exec_fname);
333 + while((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
334 + if((exec_fname && exec_fname[0] != '[') && exec_fname_length > 0) {
335 + /* not: [no active file] or no path */
336 + memcpy(fileinfo->fullpath, exec_fname, exec_fname_length);
337 + fileinfo->fullpath[exec_fname_length] = DEFAULT_SLASH;
338 + strcpy(fileinfo->fullpath +exec_fname_length +1, filename);
339 + /* apc_wprint("filename: %s, exec_fname: %s, fileinfo->fullpath: %s", filename, exec_fname, fileinfo->fullpath); */
340 + if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) {
341 + found = 1;
342 + }
343 + }
344 + }
345 +
346 + /* free the value returned by apc_tokenize */
347 + for (i = 0; paths[i]; i++) {
348 + apc_efree(paths[i]);
349 + }
350 + apc_efree(paths);
351 +
352 + return found ? 0 : -1;
353 +}
354 +
355 +/* }}} */
356 +
357 +/* {{{ regular expression wrapper functions */
358 +
359 +typedef struct {
360 + regex_t *reg;
361 + unsigned char type;
362 +} apc_regex;
363 +
364 +void* apc_regex_compile_array(char* patterns[])
365 +{
366 + apc_regex** regs;
367 + int npat;
368 + int i;
369 +
370 + if (!patterns)
371 + return NULL;
372 +
373 + /* count the number of patterns in patterns */
374 + for (npat = 0; patterns[npat] != NULL; npat++) {}
375 +
376 + if (npat == 0)
377 + return NULL;
378 +
379 + /* allocate the array of compiled expressions */
380 + regs = (apc_regex**) apc_emalloc(sizeof(apc_regex*) * (npat + 1));
381 + for (i = 0; i <= npat; i++) {
382 + regs[i] = (apc_regex *) apc_emalloc(sizeof(apc_regex));
383 + regs[i]->reg = NULL;
384 + regs[i]->type = APC_NEGATIVE_MATCH;
385 + }
386 +
387 + /* compile the expressions */
388 + for (i = 0; i < npat; i++) {
389 + char *pattern = patterns[i];
390 + if(pattern[0]=='+') { regs[i]->type = APC_POSITIVE_MATCH; pattern = patterns[i]+sizeof(char); }
391 + else if(pattern[0]=='-') { regs[i]->type = APC_NEGATIVE_MATCH; pattern = patterns[i]+sizeof(char); }
392 +
393 + regs[i]->reg = (regex_t*) apc_emalloc(sizeof(regex_t));
394 +
395 + if (regcomp(regs[i]->reg, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
396 + apc_wprint("apc_regex_compile_array: invalid expression '%s'",
397 + pattern);
398 +
399 + apc_regex_destroy_array(regs);
400 +
401 + return NULL;
402 + }
403 + }
404 +
405 + return (void*) regs;
406 +}
407 +
408 +void apc_regex_destroy_array(void* p)
409 +{
410 + if (p != NULL) {
411 + apc_regex** regs = (apc_regex**) p;
412 + int i;
413 +
414 + for (i = 0; regs[i]->reg != NULL; i++) {
415 + regfree(regs[i]->reg);
416 + apc_efree(regs[i]->reg);
417 + apc_efree(regs[i]);
418 + }
419 + apc_efree(regs);
420 + }
421 +}
422 +
423 +int apc_regex_match_array(void* p, const char* input)
424 +{
425 + apc_regex** regs;
426 + int i;
427 +
428 + if (!p)
429 + return 0;
430 +
431 + regs = (apc_regex**) p;
432 + for (i = 0; regs[i]->reg != NULL; i++)
433 + if (regexec(regs[i]->reg, input, 0, NULL, 0) == 0)
434 + return (int)(regs[i]->type);
435 +
436 + return 0;
437 +}
438 +
439 +/* }}} */
440 +
441 +/* {{{ crc32 implementation */
442 +
443 +/* this table was generated by crc32gen() */
444 +static unsigned int crc32tab[] = {
445 + /* 0 */ 0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd,
446 + /* 4 */ 0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1,
447 + /* 8 */ 0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4,
448 + /* 12 */ 0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8,
449 + /* 16 */ 0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef,
450 + /* 20 */ 0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3,
451 + /* 24 */ 0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6,
452 + /* 28 */ 0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da,
453 + /* 32 */ 0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9,
454 + /* 36 */ 0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995,
455 + /* 40 */ 0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0,
456 + /* 44 */ 0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c,
457 + /* 48 */ 0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b,
458 + /* 52 */ 0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7,
459 + /* 56 */ 0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492,
460 + /* 60 */ 0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be,
461 + /* 64 */ 0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815,
462 + /* 68 */ 0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939,
463 + /* 72 */ 0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c,
464 + /* 76 */ 0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20,
465 + /* 80 */ 0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027,
466 + /* 84 */ 0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b,
467 + /* 88 */ 0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e,
468 + /* 92 */ 0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512,
469 + /* 96 */ 0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871,
470 + /* 100 */ 0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d,
471 + /* 104 */ 0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68,
472 + /* 108 */ 0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44,
473 + /* 112 */ 0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043,
474 + /* 116 */ 0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f,
475 + /* 120 */ 0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a,
476 + /* 124 */ 0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576,
477 + /* 128 */ 0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d,
478 + /* 132 */ 0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861,
479 + /* 136 */ 0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54,
480 + /* 140 */ 0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78,
481 + /* 144 */ 0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f,
482 + /* 148 */ 0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053,
483 + /* 152 */ 0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566,
484 + /* 156 */ 0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a,
485 + /* 160 */ 0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929,
486 + /* 164 */ 0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805,
487 + /* 168 */ 0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30,
488 + /* 172 */ 0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c,
489 + /* 176 */ 0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b,
490 + /* 180 */ 0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037,
491 + /* 184 */ 0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502,
492 + /* 188 */ 0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e,
493 + /* 192 */ 0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985,
494 + /* 196 */ 0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9,
495 + /* 200 */ 0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c,
496 + /* 204 */ 0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0,
497 + /* 208 */ 0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7,
498 + /* 212 */ 0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b,
499 + /* 216 */ 0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae,
500 + /* 220 */ 0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482,
501 + /* 224 */ 0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1,
502 + /* 228 */ 0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd,
503 + /* 232 */ 0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8,
504 + /* 236 */ 0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4,
505 + /* 240 */ 0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3,
506 + /* 244 */ 0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff,
507 + /* 248 */ 0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca,
508 + /* 252 */ 0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6,
509 +};
510 +
511 +unsigned int apc_crc32(const char* buf, int len)
512 +{
513 + int i;
514 + int k;
515 + unsigned int crc;
516 +
517 + /* preconditioning */
518 + crc = 0xFFFFFFFF;
519 +
520 + for (i = 0; i < len; i++) {
521 + k = (crc ^ buf[i]) & 0x000000FF;
522 + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[k];
523 + }
524 +
525 + /* postconditioning */
526 + return ~crc;
527 +}
528 +
529 +/* crc32gen: generate the nth (0..255) crc32 table value */
530 +#if 0
531 +static unsigned long crc32gen(int n)
532 +{
533 + int i;
534 + unsigned long crc;
535 +
536 + crc = n;
537 + for (i = 8; i >= 0; i--) {
538 + if (crc & 1) {
539 + crc = (crc >> 1) ^ 0xEDB88320;
540 + }
541 + else {
542 + crc >>= 1;
543 + }
544 + }
545 + return crc;
546 +}
547 +#endif
548 +
549 +/* }}} */
550 +
551 +/*
552 + * Local variables:
553 + * tab-width: 4
554 + * c-basic-offset: 4
555 + * End:
556 + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
557 + * vim<600: expandtab sw=4 ts=4 sts=4
558 + */
559 diff -ubrN php-5.2.5-orig/ext/apc/apc_cache.c php-5.2.5/ext/apc/apc_cache.c
560 --- php-5.2.5-orig/ext/apc/apc_cache.c 1969-12-31 18:00:00.000000000 -0600
561 +++ php-5.2.5/ext/apc/apc_cache.c 2007-12-26 16:51:32.000000000 -0600
562 @@ -0,0 +1,1343 @@
563 +/*
564 + +----------------------------------------------------------------------+
565 + | APC |
566 + +----------------------------------------------------------------------+
567 + | Copyright (c) 2006 The PHP Group |
568 + +----------------------------------------------------------------------+
569 + | This source file is subject to version 3.01 of the PHP license, |
570 + | that is bundled with this package in the file LICENSE, and is |
571 + | available through the world-wide-web at the following url: |
572 + | http://www.php.net/license/3_01.txt |
573 + | If you did not receive a copy of the PHP license and are unable to |
574 + | obtain it through the world-wide-web, please send a note to |
575 + | license@php.net so we can mail you a copy immediately. |
576 + +----------------------------------------------------------------------+
577 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
578 + | Rasmus Lerdorf <rasmus@php.net> |
579 + | Arun C. Murthy <arunc@yahoo-inc.com> |
580 + | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
581 + +----------------------------------------------------------------------+
582 +
583 + This software was contributed to PHP by Community Connect Inc. in 2002
584 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
585 + Future revisions and derivatives of this source code must acknowledge
586 + Community Connect Inc. as the original contributor of this module by
587 + leaving this note intact in the source code.
588 +
589 + All other licensing and usage conditions are those of the PHP Group.
590 +
591 + */
592 +
593 +/* $Id: apc_cache.c,v 3.145 2007/10/05 23:06:56 gopalv Exp $ */
594 +
595 +#include "apc_cache.h"
596 +#include "apc_lock.h"
597 +#include "apc_sma.h"
598 +#include "apc_globals.h"
599 +#include "SAPI.h"
600 +#include "ext/standard/php_var.h"
601 +#include "ext/standard/php_smart_str.h"
602 +
603 +/* TODO: rehash when load factor exceeds threshold */
604 +
605 +#define CHECK(p) { if ((p) == NULL) return NULL; }
606 +
607 +/* {{{ locking macros */
608 +#define CREATE_LOCK(lock) apc_lck_create(NULL, 0, 1, lock)
609 +#define DESTROY_LOCK(c) apc_lck_destroy(c->header->lock)
610 +#define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c->header->lock); }
611 +#define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c->header->lock); }
612 +#define UNLOCK(c) { apc_lck_unlock(c->header->lock); HANDLE_UNBLOCK_INTERRUPTIONS(); }
613 +/* }}} */
614 +
615 +/* {{{ struct definition: slot_t */
616 +typedef struct slot_t slot_t;
617 +struct slot_t {
618 + apc_cache_key_t key; /* slot key */
619 + apc_cache_entry_t* value; /* slot value */
620 + slot_t* next; /* next slot in linked list */
621 + int num_hits; /* number of hits to this bucket */
622 + time_t creation_time; /* time slot was initialized */
623 + time_t deletion_time; /* time slot was removed from cache */
624 + time_t access_time; /* time slot was last accessed */
625 +};
626 +/* }}} */
627 +
628 +/* {{{ struct definition: header_t
629 + Any values that must be shared among processes should go in here. */
630 +typedef struct header_t header_t;
631 +struct header_t {
632 + apc_lck_t lock; /* read/write lock (exclusive blocking cache lock) */
633 + apc_lck_t wrlock; /* write lock (non-blocking used to prevent cache slams) */
634 + int num_hits; /* total successful hits in cache */
635 + int num_misses; /* total unsuccessful hits in cache */
636 + int num_inserts; /* total successful inserts in cache */
637 + slot_t* deleted_list; /* linked list of to-be-deleted slots */
638 + time_t start_time; /* time the above counters were reset */
639 + int expunges; /* total number of expunges */
640 + zend_bool busy; /* Flag to tell clients when we are busy cleaning the cache */
641 + int num_entries; /* Statistic on the number of entries */
642 + size_t mem_size; /* Statistic on the memory size used by this cache */
643 +};
644 +/* }}} */
645 +
646 +/* {{{ struct definition: apc_cache_t */
647 +struct apc_cache_t {
648 + void* shmaddr; /* process (local) address of shared cache */
649 + header_t* header; /* cache header (stored in SHM) */
650 + slot_t** slots; /* array of cache slots (stored in SHM) */
651 + int num_slots; /* number of slots in cache */
652 + int gc_ttl; /* maximum time on GC list for a slot */
653 + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */
654 +};
655 +/* }}} */
656 +
657 +/* {{{ struct definition local_slot_t */
658 +typedef struct local_slot_t local_slot_t;
659 +struct local_slot_t {
660 + slot_t *original; /* the original slot in shm */
661 + int num_hits; /* number of hits */
662 + time_t creation_time; /* creation time */
663 + apc_cache_entry_t *value; /* shallow copy of slot->value */
664 + local_slot_t *next; /* only for dead list */
665 +};
666 +/* }}} */
667 +/* {{{ struct definition apc_local_cache_t */
668 +struct apc_local_cache_t {
669 + apc_cache_t* shmcache; /* the real cache in shm */
670 + local_slot_t* slots; /* process (local) cache of objects */
671 + local_slot_t* dead_list; /* list of objects pending removal */
672 + int num_slots; /* number of slots in cache */
673 + int ttl; /* time to live */
674 + int num_hits; /* number of hits */
675 + int generation; /* every generation lives between expunges */
676 +};
677 +/* }}} */
678 +
679 +/* {{{ key_equals */
680 +#define key_equals(a, b) (a.inode==b.inode && a.device==b.device)
681 +/* }}} */
682 +
683 +/* {{{ hash */
684 +static unsigned int hash(apc_cache_key_t key)
685 +{
686 + return key.data.file.device + key.data.file.inode;
687 +}
688 +/* }}} */
689 +
690 +/* {{{ string_nhash_8 */
691 +static unsigned int string_nhash_8(const char *s, size_t len)
692 +{
693 + register const unsigned int *iv = (const unsigned int *)s;
694 + register unsigned int h = 0;
695 + register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int)));
696 +
697 + for(;iv<e;iv++) {
698 + h += *iv;
699 + h = (h << 7) | (h >> ((8*sizeof(unsigned int)) - 7));
700 + }
701 + s = (const char *)iv;
702 + for(len %= sizeof(unsigned int);len;len--) {
703 + h += *(s++);
704 + }
705 + h ^= (h >> 13);
706 + h ^= (h >> 7);
707 + return h;
708 +}
709 +/* }}} */
710 +
711 +/* {{{ make_slot */
712 +slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t)
713 +{
714 + slot_t* p = apc_sma_malloc(sizeof(slot_t));
715 + if (!p) return NULL;
716 +
717 + if(value->type == APC_CACHE_ENTRY_USER) {
718 + char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc);
719 + if (!identifier) {
720 + apc_sma_free(p);
721 + return NULL;
722 + }
723 + key.data.user.identifier = identifier;
724 + } else if(key.type == APC_CACHE_KEY_FPFILE) {
725 + char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc);
726 + if (!fullpath) {
727 + apc_sma_free(p);
728 + return NULL;
729 + }
730 + key.data.fpfile.fullpath = fullpath;
731 + }
732 + p->key = key;
733 + p->value = value;
734 + p->next = next;
735 + p->num_hits = 0;
736 + p->creation_time = t;
737 + p->access_time = t;
738 + p->deletion_time = 0;
739 + return p;
740 +}
741 +/* }}} */
742 +
743 +/* {{{ free_slot */
744 +static void free_slot(slot_t* slot)
745 +{
746 + if(slot->value->type == APC_CACHE_ENTRY_USER) {
747 + apc_sma_free((char *)slot->key.data.user.identifier);
748 + } else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
749 + apc_sma_free((char *)slot->key.data.fpfile.fullpath);
750 + }
751 + apc_cache_free_entry(slot->value);
752 + apc_sma_free(slot);
753 +}
754 +/* }}} */
755 +
756 +/* {{{ remove_slot */
757 +static void remove_slot(apc_cache_t* cache, slot_t** slot)
758 +{
759 + slot_t* dead = *slot;
760 + *slot = (*slot)->next;
761 +
762 + cache->header->mem_size -= dead->value->mem_size;
763 + cache->header->num_entries--;
764 + if (dead->value->ref_count <= 0) {
765 + free_slot(dead);
766 + }
767 + else {
768 + dead->next = cache->header->deleted_list;
769 + dead->deletion_time = time(0);
770 + cache->header->deleted_list = dead;
771 + }
772 +}
773 +/* }}} */
774 +
775 +/* {{{ process_pending_removals */
776 +static void process_pending_removals(apc_cache_t* cache)
777 +{
778 + slot_t** slot;
779 + time_t now;
780 +
781 + /* This function scans the list of removed cache entries and deletes any
782 + * entry whose reference count is zero (indicating that it is no longer
783 + * being executed) or that has been on the pending list for more than
784 + * cache->gc_ttl seconds (we issue a warning in the latter case).
785 + */
786 +
787 + if (!cache->header->deleted_list)
788 + return;
789 +
790 + slot = &cache->header->deleted_list;
791 + now = time(0);
792 +
793 + while (*slot != NULL) {
794 + int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0;
795 +
796 + if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) {
797 + slot_t* dead = *slot;
798 +
799 + if (dead->value->ref_count > 0) {
800 + switch(dead->value->type) {
801 + case APC_CACHE_ENTRY_FILE:
802 + apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) "
803 + "was on gc-list for %d seconds", dead->value->data.file.filename,
804 + dead->key.data.file.device, dead->key.data.file.inode, gc_sec);
805 + break;
806 + case APC_CACHE_ENTRY_USER:
807 + apc_log(APC_WARNING, "GC cache entry '%s' "
808 + "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec);
809 + break;
810 + }
811 + }
812 + *slot = dead->next;
813 + free_slot(dead);
814 + }
815 + else {
816 + slot = &(*slot)->next;
817 + }
818 + }
819 +}
820 +/* }}} */
821 +
822 +/* {{{ prevent_garbage_collection */
823 +static void prevent_garbage_collection(apc_cache_entry_t* entry)
824 +{
825 + /* set reference counts on zend objects to an arbitrarily high value to
826 + * prevent garbage collection after execution */
827 +
828 + enum { BIG_VALUE = 1000 };
829 +
830 + if(entry->data.file.op_array) {
831 + entry->data.file.op_array->refcount[0] = BIG_VALUE;
832 + }
833 + if (entry->data.file.functions) {
834 + int i;
835 + apc_function_t* fns = entry->data.file.functions;
836 + for (i=0; fns[i].function != NULL; i++) {
837 +#ifdef ZEND_ENGINE_2
838 + *(fns[i].function->op_array.refcount) = BIG_VALUE;
839 +#else
840 + fns[i].function->op_array.refcount[0] = BIG_VALUE;
841 +#endif
842 + }
843 + }
844 + if (entry->data.file.classes) {
845 + int i;
846 + apc_class_t* classes = entry->data.file.classes;
847 + for (i=0; classes[i].class_entry != NULL; i++) {
848 +#ifdef ZEND_ENGINE_2
849 + classes[i].class_entry->refcount = BIG_VALUE;
850 +#else
851 + classes[i].class_entry->refcount[0] = BIG_VALUE;
852 +#endif
853 + }
854 + }
855 +}
856 +/* }}} */
857 +
858 +/* {{{ apc_cache_create */
859 +apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl)
860 +{
861 + apc_cache_t* cache;
862 + int cache_size;
863 + int num_slots;
864 + int i;
865 +
866 + num_slots = size_hint > 0 ? size_hint*2 : 2000;
867 +
868 + cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t));
869 + cache_size = sizeof(header_t) + num_slots*sizeof(slot_t*);
870 +
871 + cache->shmaddr = apc_sma_malloc(cache_size);
872 + if(!cache->shmaddr) {
873 + apc_eprint("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). ");
874 + }
875 + memset(cache->shmaddr, 0, cache_size);
876 +
877 + cache->header = (header_t*) cache->shmaddr;
878 + cache->header->num_hits = 0;
879 + cache->header->num_misses = 0;
880 + cache->header->deleted_list = NULL;
881 + cache->header->start_time = time(NULL);
882 + cache->header->expunges = 0;
883 + cache->header->busy = 0;
884 +
885 + cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(header_t));
886 + cache->num_slots = num_slots;
887 + cache->gc_ttl = gc_ttl;
888 + cache->ttl = ttl;
889 + CREATE_LOCK(cache->header->lock);
890 +#if NONBLOCKING_LOCK_AVAILABLE
891 + CREATE_LOCK(cache->header->wrlock);
892 +#endif
893 + for (i = 0; i < num_slots; i++) {
894 + cache->slots[i] = NULL;
895 + }
896 +
897 + return cache;
898 +}
899 +/* }}} */
900 +
901 +/* {{{ apc_cache_destroy */
902 +void apc_cache_destroy(apc_cache_t* cache)
903 +{
904 + DESTROY_LOCK(cache);
905 + apc_efree(cache);
906 +}
907 +/* }}} */
908 +
909 +/* {{{ apc_cache_clear */
910 +void apc_cache_clear(apc_cache_t* cache)
911 +{
912 + int i;
913 +
914 + if(!cache) return;
915 +
916 + LOCK(cache);
917 + cache->header->busy = 1;
918 + cache->header->num_hits = 0;
919 + cache->header->num_misses = 0;
920 + cache->header->start_time = time(NULL);
921 + cache->header->expunges = 0;
922 +
923 + for (i = 0; i < cache->num_slots; i++) {
924 + slot_t* p = cache->slots[i];
925 + while (p) {
926 + remove_slot(cache, &p);
927 + }
928 + cache->slots[i] = NULL;
929 + }
930 +
931 + cache->header->busy = 0;
932 + UNLOCK(cache);
933 +}
934 +/* }}} */
935 +
936 +/* {{{ apc_cache_expunge */
937 +void apc_cache_expunge(apc_cache_t* cache, time_t t)
938 +{
939 + int i;
940 +
941 + if(!cache) return;
942 +
943 + if(!cache->ttl) {
944 + /*
945 + * If cache->ttl is not set, we wipe out the entire cache when
946 + * we run out of space.
947 + */
948 + LOCK(cache);
949 + cache->header->busy = 1;
950 + cache->header->expunges++;
951 + for (i = 0; i < cache->num_slots; i++) {
952 + slot_t* p = cache->slots[i];
953 + while (p) {
954 + remove_slot(cache, &p);
955 + }
956 + cache->slots[i] = NULL;
957 + }
958 + cache->header->busy = 0;
959 + UNLOCK(cache);
960 + } else {
961 + slot_t **p;
962 +
963 + /*
964 + * If the ttl for the cache is set we walk through and delete stale
965 + * entries. For the user cache that is slightly confusing since
966 + * we have the individual entry ttl's we can look at, but that would be
967 + * too much work. So if you want the user cache expunged, set a high
968 + * default apc.user_ttl and still provide a specific ttl for each entry
969 + * on insert
970 + */
971 +
972 + LOCK(cache);
973 + cache->header->busy = 1;
974 + cache->header->expunges++;
975 + for (i = 0; i < cache->num_slots; i++) {
976 + p = &cache->slots[i];
977 + while(*p) {
978 + /*
979 + * For the user cache we look at the individual entry ttl values
980 + * and if not set fall back to the default ttl for the user cache
981 + */
982 + if((*p)->value->type == APC_CACHE_ENTRY_USER) {
983 + if((*p)->value->data.user.ttl) {
984 + if((*p)->creation_time + (*p)->value->data.user.ttl < t) {
985 + remove_slot(cache, p);
986 + continue;
987 + }
988 + } else if(cache->ttl) {
989 + if((*p)->creation_time + cache->ttl < t) {
990 + remove_slot(cache, p);
991 + continue;
992 + }
993 + }
994 + } else if((*p)->access_time < (t - cache->ttl)) {
995 + remove_slot(cache, p);
996 + continue;
997 + }
998 + p = &(*p)->next;
999 + }
1000 + }
1001 + cache->header->busy = 0;
1002 + UNLOCK(cache);
1003 + }
1004 +}
1005 +/* }}} */
1006 +
1007 +/* {{{ apc_cache_insert */
1008 +int apc_cache_insert(apc_cache_t* cache,
1009 + apc_cache_key_t key,
1010 + apc_cache_entry_t* value,
1011 + time_t t)
1012 +{
1013 + slot_t** slot;
1014 +
1015 + if (!value) {
1016 + return 0;
1017 + }
1018 +
1019 +#ifdef __DEBUG_APC__
1020 + fprintf(stderr,"Inserting [%s]\n", value->data.file.filename);
1021 +#endif
1022 +
1023 + LOCK(cache);
1024 + process_pending_removals(cache);
1025 +
1026 + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
1027 + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1028 +
1029 + while(*slot) {
1030 + if(key.type == (*slot)->key.type) {
1031 + if(key.type == APC_CACHE_KEY_FILE) {
1032 + if(key_equals((*slot)->key.data.file, key.data.file)) {
1033 + /* If existing slot for the same device+inode is different, remove it and insert the new version */
1034 + if ((*slot)->key.mtime != key.mtime) {
1035 + remove_slot(cache, slot);
1036 + break;
1037 + }
1038 + UNLOCK(cache);
1039 + return 0;
1040 + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
1041 + remove_slot(cache, slot);
1042 + continue;
1043 + }
1044 + } else { /* APC_CACHE_KEY_FPFILE */
1045 + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1046 + /* Hrm.. it's already here, remove it and insert new one */
1047 + remove_slot(cache, slot);
1048 + break;
1049 + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
1050 + remove_slot(cache, slot);
1051 + continue;
1052 + }
1053 + }
1054 + }
1055 + slot = &(*slot)->next;
1056 + }
1057 +
1058 + if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
1059 + UNLOCK(cache);
1060 + return -1;
1061 + }
1062 +
1063 + cache->header->mem_size += value->mem_size;
1064 + cache->header->num_entries++;
1065 + cache->header->num_inserts++;
1066 +
1067 + UNLOCK(cache);
1068 + return 1;
1069 +}
1070 +/* }}} */
1071 +
1072 +/* {{{ apc_cache_user_insert */
1073 +int apc_cache_user_insert(apc_cache_t* cache, apc_cache_key_t key, apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC)
1074 +{
1075 + slot_t** slot;
1076 + size_t* mem_size_ptr = NULL;
1077 +
1078 + if (!value) {
1079 + return 0;
1080 + }
1081 +
1082 + LOCK(cache);
1083 + process_pending_removals(cache);
1084 +
1085 + slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots];
1086 +
1087 + if (APCG(mem_size_ptr) != NULL) {
1088 + mem_size_ptr = APCG(mem_size_ptr);
1089 + APCG(mem_size_ptr) = NULL;
1090 + }
1091 +
1092 + while (*slot) {
1093 + if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) {
1094 + /*
1095 + * At this point we have found the user cache entry. If we are doing
1096 + * an exclusive insert (apc_add) we are going to bail right away if
1097 + * the user entry already exists and it has no ttl, or
1098 + * there is a ttl and the entry has not timed out yet.
1099 + */
1100 + if(exclusive && ( !(*slot)->value->data.user.ttl ||
1101 + ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t )
1102 + ) ) {
1103 + UNLOCK(cache);
1104 + return 0;
1105 + }
1106 + remove_slot(cache, slot);
1107 + break;
1108 + } else
1109 + /*
1110 + * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of
1111 + * slot entries so we don't always have to skip past a bunch of stale entries. We check
1112 + * for staleness here and get rid of them by first checking to see if the cache has a global
1113 + * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly
1114 + * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl
1115 + */
1116 + if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) ||
1117 + ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) {
1118 + remove_slot(cache, slot);
1119 + continue;
1120 + }
1121 + slot = &(*slot)->next;
1122 + }
1123 +
1124 + if (mem_size_ptr != NULL) {
1125 + APCG(mem_size_ptr) = mem_size_ptr;
1126 + }
1127 +
1128 + if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
1129 + UNLOCK(cache);
1130 + return 0;
1131 + }
1132 + if (APCG(mem_size_ptr) != NULL) {
1133 + value->mem_size = *APCG(mem_size_ptr);
1134 + cache->header->mem_size += *APCG(mem_size_ptr);
1135 + }
1136 + cache->header->num_entries++;
1137 + cache->header->num_inserts++;
1138 +
1139 + UNLOCK(cache);
1140 + return 1;
1141 +}
1142 +/* }}} */
1143 +
1144 +/* {{{ apc_cache_find_slot */
1145 +slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t)
1146 +{
1147 + slot_t** slot;
1148 + volatile slot_t* retval = NULL;
1149 +
1150 + LOCK(cache);
1151 + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
1152 + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1153 +
1154 + while (*slot) {
1155 + if(key.type == (*slot)->key.type) {
1156 + if(key.type == APC_CACHE_KEY_FILE) {
1157 + if(key_equals((*slot)->key.data.file, key.data.file)) {
1158 + if((*slot)->key.mtime != key.mtime) {
1159 + remove_slot(cache, slot);
1160 + cache->header->num_misses++;
1161 + UNLOCK(cache);
1162 + return NULL;
1163 + }
1164 + (*slot)->num_hits++;
1165 + (*slot)->value->ref_count++;
1166 + (*slot)->access_time = t;
1167 + prevent_garbage_collection((*slot)->value);
1168 + cache->header->num_hits++;
1169 + retval = *slot;
1170 + UNLOCK(cache);
1171 + return (slot_t*)retval;
1172 + }
1173 + } else { /* APC_CACHE_KEY_FPFILE */
1174 + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1175 + /* TTL Check ? */
1176 + (*slot)->num_hits++;
1177 + (*slot)->value->ref_count++;
1178 + (*slot)->access_time = t;
1179 + prevent_garbage_collection((*slot)->value);
1180 + cache->header->num_hits++;
1181 + retval = *slot;
1182 + UNLOCK(cache);
1183 + return (slot_t*)retval;
1184 + }
1185 + }
1186 + }
1187 + slot = &(*slot)->next;
1188 + }
1189 + cache->header->num_misses++;
1190 + UNLOCK(cache);
1191 + return NULL;
1192 +}
1193 +/* }}} */
1194 +
1195 +/* {{{ apc_cache_find */
1196 +apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t)
1197 +{
1198 + slot_t * slot = apc_cache_find_slot(cache, key, t);
1199 + return (slot) ? slot->value : NULL;
1200 +}
1201 +/* }}} */
1202 +
1203 +/* {{{ apc_cache_user_find */
1204 +apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t)
1205 +{
1206 + slot_t** slot;
1207 + volatile apc_cache_entry_t* value = NULL;
1208 +
1209 + LOCK(cache);
1210 +
1211 + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
1212 +
1213 + while (*slot) {
1214 + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
1215 + /* Check to make sure this entry isn't expired by a hard TTL */
1216 + if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) {
1217 + remove_slot(cache, slot);
1218 + UNLOCK(cache);
1219 + return NULL;
1220 + }
1221 + /* Otherwise we are fine, increase counters and return the cache entry */
1222 + (*slot)->num_hits++;
1223 + (*slot)->value->ref_count++;
1224 + (*slot)->access_time = t;
1225 +
1226 + cache->header->num_hits++;
1227 + value = (*slot)->value;
1228 + UNLOCK(cache);
1229 + return (apc_cache_entry_t*)value;
1230 + }
1231 + slot = &(*slot)->next;
1232 + }
1233 +
1234 + UNLOCK(cache);
1235 + return NULL;
1236 +}
1237 +/* }}} */
1238 +
1239 +/* {{{ apc_cache_user_delete */
1240 +int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
1241 +{
1242 + slot_t** slot;
1243 +
1244 + LOCK(cache);
1245 +
1246 + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
1247 +
1248 + while (*slot) {
1249 + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
1250 + remove_slot(cache, slot);
1251 + UNLOCK(cache);
1252 + return 1;
1253 + }
1254 + slot = &(*slot)->next;
1255 + }
1256 +
1257 + UNLOCK(cache);
1258 + return 0;
1259 +}
1260 +/* }}} */
1261 +
1262 +/* {{{ apc_cache_release */
1263 +void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry)
1264 +{
1265 + /* local cache refcount-- is done in apc_local_cache_cleanup */
1266 + if(entry->local) return;
1267 +
1268 + LOCK(cache);
1269 + entry->ref_count--;
1270 + UNLOCK(cache);
1271 +}
1272 +/* }}} */
1273 +
1274 +/* {{{ apc_cache_make_file_key */
1275 +int apc_cache_make_file_key(apc_cache_key_t* key,
1276 + const char* filename,
1277 + const char* include_path,
1278 + time_t t
1279 + TSRMLS_DC)
1280 +{
1281 + struct stat *tmp_buf=NULL;
1282 + struct apc_fileinfo_t fileinfo = { {0}, };
1283 + int len;
1284 +
1285 + assert(key != NULL);
1286 +
1287 + if (!filename || !SG(request_info).path_translated) {
1288 +#ifdef __DEBUG_APC__
1289 + fprintf(stderr,"No filename and no path_translated - bailing\n");
1290 +#endif
1291 + return 0;
1292 + }
1293 +
1294 + len = strlen(filename);
1295 + if(APCG(fpstat)==0) {
1296 + if(IS_ABSOLUTE_PATH(filename,len)) {
1297 + key->data.fpfile.fullpath = filename;
1298 + key->data.fpfile.fullpath_len = len;
1299 + key->mtime = t;
1300 + key->type = APC_CACHE_KEY_FPFILE;
1301 + } else {
1302 + if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
1303 + apc_wprint("apc failed to locate %s - bailing", filename);
1304 + return 0;
1305 + }
1306 +
1307 + if(!realpath(fileinfo.fullpath, APCG(canon_path))) {
1308 + apc_wprint("realpath failed to canonicalize %s - bailing", filename);
1309 + return 0;
1310 + }
1311 +
1312 + key->data.fpfile.fullpath = APCG(canon_path);
1313 + key->data.fpfile.fullpath_len = strlen(APCG(canon_path));
1314 + key->mtime = t;
1315 + key->type = APC_CACHE_KEY_FPFILE;
1316 + }
1317 + return 1;
1318 + }
1319 +
1320 + if(!strcmp(SG(request_info).path_translated, filename)) {
1321 + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */
1322 + }
1323 + if(tmp_buf) {
1324 + fileinfo.st_buf = *tmp_buf;
1325 + } else {
1326 + if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
1327 +#ifdef __DEBUG_APC__
1328 + fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated);
1329 +#endif
1330 + return 0;
1331 + }
1332 + }
1333 +
1334 + if(APCG(max_file_size) < fileinfo.st_buf.st_size) {
1335 +#ifdef __DEBUG_APC__
1336 + fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.st_size);
1337 +#endif
1338 + return 0;
1339 + }
1340 +
1341 + /*
1342 + * This is a bit of a hack.
1343 + *
1344 + * Here I am checking to see if the file is at least 2 seconds old.
1345 + * The idea is that if the file is currently being written to then its
1346 + * mtime is going to match or at most be 1 second off of the current
1347 + * request time and we want to avoid caching files that have not been
1348 + * completely written. Of course, people should be using atomic
1349 + * mechanisms to push files onto live web servers, but adding this
1350 + * tiny safety is easier than educating the world. This is now
1351 + * configurable, but the default is still 2 seconds.
1352 + */
1353 + if(APCG(file_update_protection) && (t - fileinfo.st_buf.st_mtime < APCG(file_update_protection))) {
1354 +#ifdef __DEBUG_APC__
1355 + fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.st_mtime);
1356 +#endif
1357 + return 0;
1358 + }
1359 +
1360 + key->data.file.device = fileinfo.st_buf.st_dev;
1361 + key->data.file.inode = fileinfo.st_buf.st_ino;
1362 + /*
1363 + * If working with content management systems that like to munge the mtime,
1364 + * it might be appropriate to key off of the ctime to be immune to systems
1365 + * that try to backdate a template. If the mtime is set to something older
1366 + * than the previous mtime of a template we will obviously never see this
1367 + * "older" template. At some point the Smarty templating system did this.
1368 + * I generally disagree with using the ctime here because you lose the
1369 + * ability to warm up new content by saving it to a temporary file, hitting
1370 + * it once to cache it and then renaming it into its permanent location so
1371 + * set the apc.stat_ctime=true to enable this check.
1372 + */
1373 + if(APCG(stat_ctime)) {
1374 + key->mtime = (fileinfo.st_buf.st_ctime > fileinfo.st_buf.st_mtime) ? fileinfo.st_buf.st_ctime : fileinfo.st_buf.st_mtime;
1375 + } else {
1376 + key->mtime = fileinfo.st_buf.st_mtime;
1377 + }
1378 + key->type = APC_CACHE_KEY_FILE;
1379 + return 1;
1380 +}
1381 +/* }}} */
1382 +
1383 +/* {{{ apc_cache_make_user_key */
1384 +int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t)
1385 +{
1386 + assert(key != NULL);
1387 +
1388 + if (!identifier)
1389 + return 0;
1390 +
1391 + key->data.user.identifier = identifier;
1392 + key->data.user.identifier_len = identifier_len;
1393 + key->mtime = t;
1394 + key->type = APC_CACHE_KEY_USER;
1395 + return 1;
1396 +}
1397 +/* }}} */
1398 +
1399 +/* {{{ apc_cache_make_file_entry */
1400 +apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
1401 + zend_op_array* op_array,
1402 + apc_function_t* functions,
1403 + apc_class_t* classes)
1404 +{
1405 + apc_cache_entry_t* entry;
1406 +
1407 + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
1408 + if (!entry) return NULL;
1409 +
1410 + entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc);
1411 + if(!entry->data.file.filename) {
1412 +#ifdef __DEBUG_APC__
1413 + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n");
1414 +#endif
1415 + apc_sma_free(entry);
1416 + return NULL;
1417 + }
1418 +#ifdef __DEBUG_APC__
1419 + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename);
1420 +#endif
1421 + entry->data.file.op_array = op_array;
1422 + entry->data.file.functions = functions;
1423 + entry->data.file.classes = classes;
1424 + entry->type = APC_CACHE_ENTRY_FILE;
1425 + entry->ref_count = 0;
1426 + entry->mem_size = 0;
1427 + entry->local = 0;
1428 + return entry;
1429 +}
1430 +/* }}} */
1431 +
1432 +/* {{{ apc_cache_store_zval */
1433 +zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
1434 +{
1435 + smart_str buf = {0};
1436 + php_serialize_data_t var_hash;
1437 + TSRMLS_FETCH();
1438 +
1439 + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1440 + if(!dst) {
1441 + CHECK(dst = (zval*) allocate(sizeof(zval)));
1442 + }
1443 +
1444 + PHP_VAR_SERIALIZE_INIT(var_hash);
1445 + php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC);
1446 + PHP_VAR_SERIALIZE_DESTROY(var_hash);
1447 +
1448 + dst->type = IS_NULL; /* in case we fail */
1449 + if(buf.c) {
1450 + dst->type = src->type & ~IS_CONSTANT_INDEX;
1451 + dst->value.str.len = buf.len;
1452 + CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate));
1453 + dst->type = src->type;
1454 + smart_str_free(&buf);
1455 + }
1456 + return dst;
1457 + } else {
1458 +
1459 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1460 + HashTable *old = APCG(copied_zvals);
1461 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1462 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1463 +
1464 + dst = apc_copy_zval(dst, src, allocate, deallocate);
1465 +
1466 + if(APCG(copied_zvals)) {
1467 + zend_hash_destroy(APCG(copied_zvals));
1468 + efree(APCG(copied_zvals));
1469 + }
1470 +
1471 + APCG(copied_zvals) = old;
1472 +
1473 + return dst;
1474 + }
1475 +}
1476 +/* }}} */
1477 +
1478 +/* {{{ apc_cache_fetch_zval */
1479 +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
1480 +{
1481 + TSRMLS_FETCH();
1482 + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1483 + php_unserialize_data_t var_hash;
1484 + const unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
1485 +
1486 + PHP_VAR_UNSERIALIZE_INIT(var_hash);
1487 + if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) {
1488 + PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1489 + zval_dtor(dst);
1490 + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - Z_STRVAL_P(src)), Z_STRLEN_P(src));
1491 + dst->type = IS_NULL;
1492 + }
1493 + PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1494 + return dst;
1495 + } else {
1496 +
1497 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1498 + HashTable *old = APCG(copied_zvals);
1499 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1500 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1501 +
1502 + dst = apc_copy_zval(dst, src, allocate, deallocate);
1503 +
1504 + if(APCG(copied_zvals)) {
1505 + zend_hash_destroy(APCG(copied_zvals));
1506 + efree(APCG(copied_zvals));
1507 + }
1508 +
1509 + APCG(copied_zvals) = old;
1510 +
1511 + return dst;
1512 + }
1513 +}
1514 +/* }}} */
1515 +
1516 +/* {{{ apc_cache_free_zval */
1517 +void apc_cache_free_zval(zval* src, apc_free_t deallocate)
1518 +{
1519 + TSRMLS_FETCH();
1520 + if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1521 + if (src->value.str.val) {
1522 + deallocate(src->value.str.val);
1523 + }
1524 + deallocate(src);
1525 + } else {
1526 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1527 + HashTable *old = APCG(copied_zvals);
1528 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1529 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1530 +
1531 + apc_free_zval(src, deallocate);
1532 +
1533 + if(APCG(copied_zvals)) {
1534 + zend_hash_destroy(APCG(copied_zvals));
1535 + efree(APCG(copied_zvals));
1536 + }
1537 +
1538 + APCG(copied_zvals) = old;
1539 + }
1540 +}
1541 +/* }}} */
1542 +
1543 +/* {{{ apc_cache_make_user_entry */
1544 +apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl)
1545 +{
1546 + apc_cache_entry_t* entry;
1547 +
1548 + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
1549 + if (!entry) return NULL;
1550 +
1551 + entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc);
1552 + entry->data.user.info_len = info_len;
1553 + if(!entry->data.user.info) {
1554 + apc_sma_free(entry);
1555 + return NULL;
1556 + }
1557 + entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free);
1558 + if(!entry->data.user.val) {
1559 + apc_sma_free(entry->data.user.info);
1560 + apc_sma_free(entry);
1561 + return NULL;
1562 + }
1563 + INIT_PZVAL(entry->data.user.val);
1564 + entry->data.user.ttl = ttl;
1565 + entry->type = APC_CACHE_ENTRY_USER;
1566 + entry->ref_count = 0;
1567 + entry->mem_size = 0;
1568 + entry->local = 0;
1569 + return entry;
1570 +}
1571 +/* }}} */
1572 +
1573 +/* {{{ apc_cache_free_entry */
1574 +void apc_cache_free_entry(apc_cache_entry_t* entry)
1575 +{
1576 + if (entry != NULL) {
1577 + assert(entry->ref_count == 0);
1578 + switch(entry->type) {
1579 + case APC_CACHE_ENTRY_FILE:
1580 + apc_sma_free(entry->data.file.filename);
1581 + apc_free_op_array(entry->data.file.op_array, apc_sma_free);
1582 + apc_free_functions(entry->data.file.functions, apc_sma_free);
1583 + apc_free_classes(entry->data.file.classes, apc_sma_free);
1584 + break;
1585 + case APC_CACHE_ENTRY_USER:
1586 + apc_sma_free(entry->data.user.info);
1587 + apc_cache_free_zval(entry->data.user.val, apc_sma_free);
1588 + break;
1589 + }
1590 + apc_sma_free(entry);
1591 + }
1592 +}
1593 +/* }}} */
1594 +
1595 +/* {{{ apc_cache_info */
1596 +apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited)
1597 +{
1598 + apc_cache_info_t* info;
1599 + slot_t* p;
1600 + int i;
1601 +
1602 + if(!cache) return NULL;
1603 +
1604 + LOCK(cache);
1605 +
1606 + info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t));
1607 + if(!info) {
1608 + UNLOCK(cache);
1609 + return NULL;
1610 + }
1611 + info->num_slots = cache->num_slots;
1612 + info->ttl = cache->ttl;
1613 + info->num_hits = cache->header->num_hits;
1614 + info->num_misses = cache->header->num_misses;
1615 + info->list = NULL;
1616 + info->deleted_list = NULL;
1617 + info->start_time = cache->header->start_time;
1618 + info->expunges = cache->header->expunges;
1619 + info->mem_size = cache->header->mem_size;
1620 + info->num_entries = cache->header->num_entries;
1621 + info->num_inserts = cache->header->num_inserts;
1622 +
1623 + if(!limited) {
1624 + /* For each hashtable slot */
1625 + for (i = 0; i < info->num_slots; i++) {
1626 + p = cache->slots[i];
1627 + for (; p != NULL; p = p->next) {
1628 + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1629 +
1630 + if(p->value->type == APC_CACHE_ENTRY_FILE) {
1631 + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1632 + link->data.file.device = p->key.data.file.device;
1633 + link->data.file.inode = p->key.data.file.inode;
1634 + link->type = APC_CACHE_ENTRY_FILE;
1635 + } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1636 + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1637 + link->data.user.ttl = p->value->data.user.ttl;
1638 + link->type = APC_CACHE_ENTRY_USER;
1639 + }
1640 + link->num_hits = p->num_hits;
1641 + link->mtime = p->key.mtime;
1642 + link->creation_time = p->creation_time;
1643 + link->deletion_time = p->deletion_time;
1644 + link->access_time = p->access_time;
1645 + link->ref_count = p->value->ref_count;
1646 + link->mem_size = p->value->mem_size;
1647 + link->next = info->list;
1648 + info->list = link;
1649 + }
1650 + }
1651 +
1652 + /* For each slot pending deletion */
1653 + for (p = cache->header->deleted_list; p != NULL; p = p->next) {
1654 + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1655 +
1656 + if(p->value->type == APC_CACHE_ENTRY_FILE) {
1657 + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1658 + if(p->key.type == APC_CACHE_KEY_FILE) {
1659 + link->data.file.device = p->key.data.file.device;
1660 + link->data.file.inode = p->key.data.file.inode;
1661 + } else { /* This is a no-stat fullpath file entry */
1662 + link->data.file.device = 0;
1663 + link->data.file.inode = 0;
1664 + }
1665 + link->type = APC_CACHE_ENTRY_FILE;
1666 + } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1667 + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1668 + link->data.user.ttl = p->value->data.user.ttl;
1669 + link->type = APC_CACHE_ENTRY_USER;
1670 + }
1671 + link->num_hits = p->num_hits;
1672 + link->mtime = p->key.mtime;
1673 + link->creation_time = p->creation_time;
1674 + link->deletion_time = p->deletion_time;
1675 + link->access_time = p->access_time;
1676 + link->ref_count = p->value->ref_count;
1677 + link->mem_size = p->value->mem_size;
1678 + link->next = info->deleted_list;
1679 + info->deleted_list = link;
1680 + }
1681 + }
1682 +
1683 + UNLOCK(cache);
1684 + return info;
1685 +}
1686 +/* }}} */
1687 +
1688 +/* {{{ apc_cache_free_info */
1689 +void apc_cache_free_info(apc_cache_info_t* info)
1690 +{
1691 + apc_cache_link_t* p = info->list;
1692 + apc_cache_link_t* q = NULL;
1693 + while (p != NULL) {
1694 + q = p;
1695 + p = p->next;
1696 + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1697 + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1698 + apc_efree(q);
1699 + }
1700 + p = info->deleted_list;
1701 + while (p != NULL) {
1702 + q = p;
1703 + p = p->next;
1704 + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1705 + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1706 + apc_efree(q);
1707 + }
1708 + apc_efree(info);
1709 +}
1710 +/* }}} */
1711 +
1712 +/* {{{ apc_cache_unlock */
1713 +void apc_cache_unlock(apc_cache_t* cache)
1714 +{
1715 + UNLOCK(cache);
1716 +}
1717 +/* }}} */
1718 +
1719 +/* {{{ apc_cache_busy */
1720 +zend_bool apc_cache_busy(apc_cache_t* cache)
1721 +{
1722 + return cache->header->busy;
1723 +}
1724 +/* }}} */
1725 +
1726 +#if NONBLOCKING_LOCK_AVAILABLE
1727 +/* {{{ apc_cache_write_lock */
1728 +zend_bool apc_cache_write_lock(apc_cache_t* cache)
1729 +{
1730 + return apc_lck_nb_lock(cache->header->wrlock);
1731 +}
1732 +/* }}} */
1733 +
1734 +/* {{{ apc_cache_write_unlock */
1735 +void apc_cache_write_unlock(apc_cache_t* cache)
1736 +{
1737 + apc_lck_unlock(cache->header->wrlock);
1738 +}
1739 +/* }}} */
1740 +#endif
1741 +
1742 +/* {{{ make_local_slot */
1743 +static local_slot_t* make_local_slot(apc_local_cache_t* cache, local_slot_t* lslot, slot_t* slot, time_t t)
1744 +{
1745 + apc_cache_entry_t* value;
1746 +
1747 + value = apc_emalloc(sizeof(apc_cache_entry_t));
1748 + memcpy(value, slot->value, sizeof(apc_cache_entry_t)); /* bitwise copy */
1749 + value->local = 1;
1750 +
1751 + lslot->original = slot;
1752 + lslot->value = value;
1753 + lslot->num_hits = 0;
1754 + lslot->creation_time = t;
1755 +
1756 + return lslot; /* for what joy ? ... consistency */
1757 +}
1758 +/* }}} */
1759 +
1760 +/* {{{ free_local_slot */
1761 +static void free_local_slot(apc_local_cache_t* cache, local_slot_t* lslot)
1762 +{
1763 + local_slot_t * dead = NULL;
1764 + if(!lslot->original) return;
1765 +
1766 + /* TODO: Bad design to allocate memory in a free_* - fix when bored (hehe) */
1767 + dead = apc_emalloc(sizeof(local_slot_t));
1768 + memcpy(dead, lslot, sizeof(local_slot_t)); /* bitwise copy */
1769 +
1770 + lslot->original = NULL;
1771 + lslot->value = NULL;
1772 +
1773 + dead->next = cache->dead_list;
1774 + cache->dead_list = dead;
1775 +}
1776 +/* }}} */
1777 +
1778 +/* {{{ apc_local_cache_create */
1779 +apc_local_cache_t* apc_local_cache_create(apc_cache_t *shmcache, int num_slots, int ttl)
1780 +{
1781 + apc_local_cache_t* cache = NULL;
1782 +
1783 + cache = (apc_local_cache_t*) apc_emalloc(sizeof(apc_local_cache_t));
1784 +
1785 + cache->slots = (local_slot_t*) (apc_emalloc(sizeof(local_slot_t) * num_slots));
1786 + memset(cache->slots, 0, sizeof(local_slot_t) * num_slots);
1787 +
1788 + cache->shmcache = shmcache;
1789 + cache->num_slots = num_slots;
1790 + cache->ttl = ttl;
1791 + cache->num_hits = 0;
1792 + cache->generation = shmcache->header->expunges;
1793 + cache->dead_list = NULL;
1794 +
1795 + return cache;
1796 +}
1797 +/* }}} */
1798 +
1799 +/* {{{ apc_local_cache_cleanup */
1800 +void apc_local_cache_cleanup(apc_local_cache_t* cache) {
1801 + local_slot_t * lslot;
1802 + time_t t = time(0);
1803 +
1804 + int i;
1805 + for(i = 0; i < cache->num_slots; i++) {
1806 + lslot = &cache->slots[i];
1807 + /* every slot lives for exactly TTL seconds */
1808 + if((lslot->original && lslot->creation_time < (t - cache->ttl)) ||
1809 + cache->generation != cache->shmcache->header->expunges) {
1810 + free_local_slot(cache, lslot);
1811 + }
1812 + }
1813 +
1814 + LOCK(cache->shmcache);
1815 + for(lslot = cache->dead_list; lslot != NULL; lslot = lslot->next) {
1816 + lslot->original->num_hits += lslot->num_hits;
1817 + lslot->original->value->ref_count--; /* apc_cache_release(cache->shmcache, lslot->original->value); */
1818 + apc_efree(lslot->value);
1819 + }
1820 + UNLOCK(cache->shmcache);
1821 +
1822 + cache->dead_list = NULL;
1823 +}
1824 +/* }}} */
1825 +
1826 +/* {{{ apc_local_cache_destroy */
1827 +void apc_local_cache_destroy(apc_local_cache_t* cache)
1828 +{
1829 + int i;
1830 + for(i = 0; i < cache->num_slots; i++) {
1831 + free_local_slot(cache, &cache->slots[i]);
1832 + }
1833 +
1834 + apc_local_cache_cleanup(cache);
1835 +
1836 + LOCK(cache->shmcache);
1837 + cache->shmcache->header->num_hits += cache->num_hits;
1838 + UNLOCK(cache->shmcache);
1839 +
1840 + apc_efree(cache->slots);
1841 + apc_efree(cache);
1842 +}
1843 +/* }}} */
1844 +
1845 +/* {{{ apc_local_cache_find */
1846 +apc_cache_entry_t* apc_local_cache_find(apc_local_cache_t* cache, apc_cache_key_t key, time_t t)
1847 +{
1848 + slot_t* slot;
1849 + local_slot_t* lslot;
1850 +
1851 + if(key.type == APC_CACHE_KEY_FILE) lslot = &cache->slots[hash(key) % cache->num_slots];
1852 + else lslot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1853 +
1854 + slot = lslot->original;
1855 +
1856 + if(slot && key.type == slot->key.type) {
1857 + if(slot->access_time < (t - cache->ttl)) {
1858 + goto not_found;
1859 + }
1860 + if(key.type == APC_CACHE_KEY_FILE &&
1861 + key_equals(slot->key.data.file, key.data.file)) {
1862 + if(slot->key.mtime != key.mtime) {
1863 + free_local_slot(cache, lslot);
1864 + goto not_found;
1865 + }
1866 + cache->num_hits++;
1867 + lslot->num_hits++;
1868 + lslot->original->access_time = t; /* unlocked write, but last write wins */
1869 + return lslot->value;
1870 + } else if(key.type == APC_CACHE_KEY_FPFILE) {
1871 + if(!memcmp(slot->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1872 + cache->num_hits++;
1873 + lslot->num_hits++;
1874 + lslot->original->access_time = t; /* unlocked write, but last write wins */
1875 + return lslot->value;
1876 + }
1877 + }
1878 + }
1879 +not_found:
1880 + if(apc_cache_busy(cache->shmcache)) {
1881 + return NULL;
1882 + }
1883 +
1884 + slot = apc_cache_find_slot(cache->shmcache, key, t);
1885 +
1886 + if(!slot) return NULL;
1887 +
1888 + /* i.e maintain a sort of top list */
1889 + if(lslot->original == NULL || (lslot->original->num_hits + lslot->num_hits) < slot->num_hits) {
1890 + free_local_slot(cache, lslot);
1891 + make_local_slot(cache, lslot, slot, t);
1892 + return lslot->value;
1893 + }
1894 + return slot->value;
1895 +}
1896 +/* }}} */
1897 +
1898 +/*
1899 + * Local variables:
1900 + * tab-width: 4
1901 + * c-basic-offset: 4
1902 + * End:
1903 + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
1904 + * vim<600: expandtab sw=4 ts=4 sts=4
1905 + */
1906 diff -ubrN php-5.2.5-orig/ext/apc/apc_cache.h php-5.2.5/ext/apc/apc_cache.h
1907 --- php-5.2.5-orig/ext/apc/apc_cache.h 1969-12-31 18:00:00.000000000 -0600
1908 +++ php-5.2.5/ext/apc/apc_cache.h 2007-12-26 16:51:32.000000000 -0600
1909 @@ -0,0 +1,312 @@
1910 +/*
1911 + +----------------------------------------------------------------------+
1912 + | APC |
1913 + +----------------------------------------------------------------------+
1914 + | Copyright (c) 2006 The PHP Group |
1915 + +----------------------------------------------------------------------+
1916 + | This source file is subject to version 3.01 of the PHP license, |
1917 + | that is bundled with this package in the file LICENSE, and is |
1918 + | available through the world-wide-web at the following url: |
1919 + | http://www.php.net/license/3_01.txt. |
1920 + | If you did not receive a copy of the PHP license and are unable to |
1921 + | obtain it through the world-wide-web, please send a note to |
1922 + | license@php.net so we can mail you a copy immediately. |
1923 + +----------------------------------------------------------------------+
1924 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
1925 + | Rasmus Lerdorf <rasmus@php.net> |
1926 + +----------------------------------------------------------------------+
1927 +
1928 + This software was contributed to PHP by Community Connect Inc. in 2002
1929 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
1930 + Future revisions and derivatives of this source code must acknowledge
1931 + Community Connect Inc. as the original contributor of this module by
1932 + leaving this note intact in the source code.
1933 +
1934 + All other licensing and usage conditions are those of the PHP Group.
1935 +
1936 + */
1937 +
1938 +/* $Id: apc_cache.h,v 3.46 2007/10/05 23:06:56 gopalv Exp $ */
1939 +
1940 +#ifndef APC_CACHE_H
1941 +#define APC_CACHE_H
1942 +
1943 +/*
1944 + * This module defines the shared memory file cache. Basically all of the
1945 + * logic for storing and retrieving cache entries lives here.
1946 + */
1947 +
1948 +#include "apc.h"
1949 +#include "apc_compile.h"
1950 +
1951 +#define APC_CACHE_ENTRY_FILE 1
1952 +#define APC_CACHE_ENTRY_USER 2
1953 +
1954 +#define APC_CACHE_KEY_FILE 1
1955 +#define APC_CACHE_KEY_USER 2
1956 +#define APC_CACHE_KEY_FPFILE 3
1957 +
1958 +/* {{{ struct definition: apc_cache_key_t */
1959 +#define T apc_cache_t*
1960 +typedef struct apc_cache_t apc_cache_t; /* opaque cache type */
1961 +
1962 +typedef union _apc_cache_key_data_t {
1963 + struct {
1964 + dev_t device; /* the filesystem device */
1965 + ino_t inode; /* the filesystem inode */
1966 + } file;
1967 + struct {
1968 + const char *identifier;
1969 + int identifier_len;
1970 + } user;
1971 + struct {
1972 + const char *fullpath;
1973 + int fullpath_len;
1974 + } fpfile;
1975 +} apc_cache_key_data_t;
1976 +
1977 +typedef struct apc_cache_key_t apc_cache_key_t;
1978 +struct apc_cache_key_t {
1979 + apc_cache_key_data_t data;
1980 + time_t mtime; /* the mtime of this cached entry */
1981 + unsigned char type;
1982 +};
1983 +/* }}} */
1984 +
1985 +/* {{{ struct definition: apc_cache_entry_t */
1986 +typedef union _apc_cache_entry_value_t {
1987 + struct {
1988 + char *filename; /* absolute path to source file */
1989 + zend_op_array* op_array; /* op_array allocated in shared memory */
1990 + apc_function_t* functions; /* array of apc_function_t's */
1991 + apc_class_t* classes; /* array of apc_class_t's */
1992 + } file;
1993 + struct {
1994 + char *info;
1995 + int info_len;
1996 + zval *val;
1997 + unsigned int ttl;
1998 + } user;
1999 +} apc_cache_entry_value_t;
2000 +
2001 +typedef struct apc_cache_entry_t apc_cache_entry_t;
2002 +struct apc_cache_entry_t {
2003 + apc_cache_entry_value_t data;
2004 + unsigned char type;
2005 + unsigned char local;
2006 + int ref_count;
2007 + size_t mem_size;
2008 +};
2009 +/* }}} */
2010 +
2011 +/*
2012 + * apc_cache_create creates the shared memory compiler cache. This function
2013 + * should be called just once (ideally in the web server parent process, e.g.
2014 + * in apache), otherwise you will end up with multiple caches (which won't
2015 + * necessarily break anything). Returns a pointer to the cache object.
2016 + *
2017 + * size_hint is a "hint" at the total number of source files that will be
2018 + * cached. It determines the physical size of the hash table. Passing 0 for
2019 + * this argument will use a reasonable default value.
2020 + *
2021 + * gc_ttl is the maximum time a cache entry may speed on the garbage
2022 + * collection list. This is basically a work around for the inherent
2023 + * unreliability of our reference counting mechanism (see apc_cache_release).
2024 + *
2025 + * ttl is the maximum time a cache entry can idle in a slot in case the slot
2026 + * is needed. This helps in cleaning up the cache and ensuring that entries
2027 + * hit frequently stay cached and ones not hit very often eventually disappear.
2028 + */
2029 +extern T apc_cache_create(int size_hint, int gc_ttl, int ttl);
2030 +
2031 +/*
2032 + * apc_cache_destroy releases any OS resources associated with a cache object.
2033 + * Under apache, this function can be safely called by the child processes
2034 + * when they exit.
2035 + */
2036 +extern void apc_cache_destroy(T cache);
2037 +
2038 +/*
2039 + * apc_cache_clear empties a cache. This can safely be called at any time,
2040 + * even while other server processes are executing cached source files.
2041 + */
2042 +extern void apc_cache_clear(T cache);
2043 +
2044 +/*
2045 + * apc_cache_insert adds an entry to the cache, using a filename as a key.
2046 + * Internally, the filename is translated to a canonical representation, so
2047 + * that relative and absolute filenames will map to a single key. Returns
2048 + * non-zero if the file was successfully inserted, 0 otherwise. If 0 is
2049 + * returned, the caller must free the cache entry by calling
2050 + * apc_cache_free_entry (see below).
2051 + *
2052 + * key is the value created by apc_cache_make_file_key for file keys.
2053 + *
2054 + * value is a cache entry returned by apc_cache_make_entry (see below).
2055 + */
2056 +extern int apc_cache_insert(T cache, apc_cache_key_t key,
2057 + apc_cache_entry_t* value, time_t t);
2058 +
2059 +extern int apc_cache_user_insert(T cache, apc_cache_key_t key,
2060 + apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC);
2061 +
2062 +/*
2063 + * apc_cache_find searches for a cache entry by filename, and returns a
2064 + * pointer to the entry if found, NULL otherwise.
2065 + *
2066 + * key is a value created by apc_cache_make_file_key for file keys.
2067 + */
2068 +extern apc_cache_entry_t* apc_cache_find(T cache, apc_cache_key_t key, time_t t);
2069 +
2070 +/*
2071 + * apc_cache_user_find searches for a cache entry by its hashed identifier,
2072 + * and returns a pointer to the entry if found, NULL otherwise.
2073 + *
2074 + */
2075 +extern apc_cache_entry_t* apc_cache_user_find(T cache, char* strkey, int keylen, time_t t);
2076 +
2077 +/*
2078 + * apc_cache_user_delete finds an entry in the user cache and deletes it.
2079 + */
2080 +extern int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen);
2081 +
2082 +/* apc_cach_fetch_zval takes a zval in the cache and reconstructs a runtime
2083 + * zval from it.
2084 + *
2085 + */
2086 +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate);
2087 +
2088 +/*
2089 + * apc_cache_release decrements the reference count associated with a cache
2090 + * entry. Calling apc_cache_find automatically increments the reference count,
2091 + * and this function must be called post-execution to return the count to its
2092 + * original value. Failing to do so will prevent the entry from being
2093 + * garbage-collected.
2094 + *
2095 + * entry is the cache entry whose ref count you want to decrement.
2096 + */
2097 +extern void apc_cache_release(T cache, apc_cache_entry_t* entry);
2098 +
2099 +/*
2100 + * apc_cache_make_file_key creates a key object given a relative or absolute
2101 + * filename and an optional list of auxillary paths to search. include_path is
2102 + * searched if the filename cannot be found relative to the current working
2103 + * directory.
2104 + *
2105 + * key points to caller-allocated storage (must not be null).
2106 + *
2107 + * filename is the path to the source file.
2108 + *
2109 + * include_path is a colon-separated list of directories to search.
2110 + *
2111 + * and finally we pass in the current request time so we can avoid
2112 + * caching files with a current mtime which tends to indicate that
2113 + * they are still being written to.
2114 + */
2115 +extern int apc_cache_make_file_key(apc_cache_key_t* key,
2116 + const char* filename,
2117 + const char* include_path,
2118 + time_t t
2119 + TSRMLS_DC);
2120 +
2121 +/*
2122 + * apc_cache_make_file_entry creates an apc_cache_entry_t object given a filename
2123 + * and the compilation results returned by the PHP compiler.
2124 + */
2125 +extern apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
2126 + zend_op_array* op_array,
2127 + apc_function_t* functions,
2128 + apc_class_t* classes);
2129 +/*
2130 + * apc_cache_make_user_entry creates an apc_cache_entry_t object given an info string
2131 + * and the zval to be stored.
2132 + */
2133 +extern apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval *val, const unsigned int ttl);
2134 +
2135 +extern int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t);
2136 +
2137 +/*
2138 + * Frees all memory associated with an object returned by apc_cache_make_entry
2139 + * (see above).
2140 + */
2141 +extern void apc_cache_free_entry(apc_cache_entry_t* entry);
2142 +
2143 +/* {{{ struct definition: apc_cache_link_data_t */
2144 +typedef union _apc_cache_link_data_t {
2145 + struct {
2146 + char *filename;
2147 + dev_t device;
2148 + ino_t inode;
2149 + } file;
2150 + struct {
2151 + char *info;
2152 + unsigned int ttl;
2153 + } user;
2154 +} apc_cache_link_data_t;
2155 +/* }}} */
2156 +
2157 +/* {{{ struct definition: apc_cache_link_t */
2158 +typedef struct apc_cache_link_t apc_cache_link_t;
2159 +struct apc_cache_link_t {
2160 + apc_cache_link_data_t data;
2161 + unsigned char type;
2162 + int num_hits;
2163 + time_t mtime;
2164 + time_t creation_time;
2165 + time_t deletion_time;
2166 + time_t access_time;
2167 + int ref_count;
2168 + size_t mem_size;
2169 + apc_cache_link_t* next;
2170 +};
2171 +/* }}} */
2172 +
2173 +/* {{{ struct definition: apc_cache_info_t */
2174 +typedef struct apc_cache_info_t apc_cache_info_t;
2175 +struct apc_cache_info_t {
2176 + int num_slots;
2177 + int num_hits;
2178 + int num_misses;
2179 + int ttl;
2180 + apc_cache_link_t* list;
2181 + apc_cache_link_t* deleted_list;
2182 + time_t start_time;
2183 + int expunges;
2184 + int num_entries;
2185 + int num_inserts;
2186 + size_t mem_size;
2187 +};
2188 +/* }}} */
2189 +
2190 +extern apc_cache_info_t* apc_cache_info(T cache, zend_bool limited);
2191 +extern void apc_cache_free_info(apc_cache_info_t* info);
2192 +extern void apc_cache_expunge(apc_cache_t* cache, time_t t);
2193 +extern void apc_cache_unlock(apc_cache_t* cache);
2194 +extern zend_bool apc_cache_busy(apc_cache_t* cache);
2195 +extern zend_bool apc_cache_write_lock(apc_cache_t* cache);
2196 +extern void apc_cache_write_unlock(apc_cache_t* cache);
2197 +
2198 +/*
2199 + * Process local cache, which keeps a refcount hungry version of the slots
2200 + * for quick access without a lock - as long as the entry exists in local
2201 + * cache, the refcount of the shm version will be +1 more than required.
2202 + * It holds no data, only a shallow copy of apc_cache_entry.
2203 + */
2204 +typedef struct apc_local_cache_t apc_local_cache_t; /* process-local cache */
2205 +
2206 +extern apc_local_cache_t* apc_local_cache_create(apc_cache_t *shmcache, int num_slots, int ttl);
2207 +extern apc_cache_entry_t* apc_local_cache_find(apc_local_cache_t* cache, apc_cache_key_t key, time_t t);
2208 +extern void apc_local_cache_destroy(apc_local_cache_t* cache);
2209 +extern void apc_local_cache_cleanup(apc_local_cache_t* cache);
2210 +
2211 +#undef T
2212 +#endif
2213 +
2214 +/*
2215 + * Local variables:
2216 + * tab-width: 4
2217 + * c-basic-offset: 4
2218 + * End:
2219 + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
2220 + * vim<600: expandtab sw=4 ts=4 sts=4
2221 + */
2222 diff -ubrN php-5.2.5-orig/ext/apc/apc_compile.c php-5.2.5/ext/apc/apc_compile.c
2223 --- php-5.2.5-orig/ext/apc/apc_compile.c 1969-12-31 18:00:00.000000000 -0600
2224 +++ php-5.2.5/ext/apc/apc_compile.c 2007-12-26 16:51:32.000000000 -0600
2225 @@ -0,0 +1,2529 @@
2226 +/*
2227 + +----------------------------------------------------------------------+
2228 + | APC |
2229 + +----------------------------------------------------------------------+
2230 + | Copyright (c) 2006 The PHP Group |
2231 + +----------------------------------------------------------------------+
2232 + | This source file is subject to version 3.01 of the PHP license, |
2233 + | that is bundled with this package in the file LICENSE, and is |
2234 + | available through the world-wide-web at the following url: |
2235 + | http://www.php.net/license/3_01.txt. |
2236 + | If you did not receive a copy of the PHP license and are unable to |
2237 + | obtain it through the world-wide-web, please send a note to |
2238 + | license@php.net so we can mail you a copy immediately. |
2239 + +----------------------------------------------------------------------+
2240 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
2241 + | Rasmus Lerdorf <rasmus@php.net> |
2242 + | Arun C. Murthy <arunc@yahoo-inc.com> |
2243 + | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
2244 + +----------------------------------------------------------------------+
2245 +
2246 + This software was contributed to PHP by Community Connect Inc. in 2002
2247 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
2248 + Future revisions and derivatives of this source code must acknowledge
2249 + Community Connect Inc. as the original contributor of this module by
2250 + leaving this note intact in the source code.
2251 +
2252 + All other licensing and usage conditions are those of the PHP Group.
2253 +
2254 + */
2255 +
2256 +/* $Id: apc_compile.c,v 3.87 2007/08/25 13:09:13 gopalv Exp $ */
2257 +
2258 +#include "apc_compile.h"
2259 +#include "apc_globals.h"
2260 +#include "apc_zend.h"
2261 +
2262 +typedef void* (*ht_copy_fun_t)(void*, void*, apc_malloc_t, apc_free_t);
2263 +typedef void (*ht_free_fun_t)(void*, apc_free_t);
2264 +typedef int (*ht_check_copy_fun_t)(Bucket*, va_list);
2265 +
2266 +#ifdef ZEND_ENGINE_2
2267 +typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*);
2268 +#endif
2269 +
2270 +#define CHECK(p) { if ((p) == NULL) return NULL; }
2271 +
2272 +/* {{{ internal function declarations */
2273 +
2274 +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size);
2275 +
2276 +static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_malloc_t);
2277 +
2278 +/*
2279 + * The "copy" functions perform deep-copies on a particular data structure
2280 + * (passed as the second argument). They also optionally allocate space for
2281 + * the destination data structure if the first argument is null.
2282 + */
2283 +static zval** my_copy_zval_ptr(zval**, const zval**, apc_malloc_t, apc_free_t);
2284 +static zval* my_copy_zval(zval*, const zval*, apc_malloc_t, apc_free_t);
2285 +static znode* my_copy_znode(znode*, znode*, apc_malloc_t, apc_free_t);
2286 +static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_malloc_t, apc_free_t);
2287 +static zend_function* my_copy_function(zend_function*, zend_function*, apc_malloc_t, apc_free_t);
2288 +static zend_function_entry* my_copy_function_entry(zend_function_entry*, zend_function_entry*, apc_malloc_t, apc_free_t);
2289 +static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_malloc_t, apc_free_t);
2290 +static HashTable* my_copy_hashtable_ex(HashTable*, HashTable*, ht_copy_fun_t, ht_free_fun_t, int, apc_malloc_t, apc_free_t, ht_check_copy_fun_t, ...);
2291 +#define my_copy_hashtable( dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate) \
2292 + my_copy_hashtable_ex(dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate, NULL)
2293 +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate);
2294 +#ifdef ZEND_ENGINE_2
2295 +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate);
2296 +static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, zend_arg_info*, uint, apc_malloc_t, apc_free_t);
2297 +static zend_arg_info* my_copy_arg_info(zend_arg_info*, zend_arg_info*, apc_malloc_t, apc_free_t);
2298 +#endif
2299 +/*
2300 + * The "destroy" functions free the memory associated with a particular data
2301 + * structure but do not free the pointer to the data structure.
2302 + *
2303 + * my_destroy_zval() returns SUCCESS or FAILURE, FAILURE means that
2304 + * the zval* has other references elsewhere
2305 + */
2306 +static int my_destroy_zval(zval*, apc_free_t);
2307 +static void my_destroy_zval_ptr(zval**, apc_free_t);
2308 +static void my_destroy_zend_op(zend_op*, apc_free_t);
2309 +static void my_destroy_znode(znode*, apc_free_t);
2310 +static void my_destroy_function(zend_function*, apc_free_t);
2311 +static void my_destroy_function_entry(zend_function_entry*, apc_free_t);
2312 +static void my_destroy_class_entry(zend_class_entry*, apc_free_t);
2313 +static void my_destroy_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
2314 +static void my_destroy_op_array(zend_op_array*, apc_free_t);
2315 +#ifdef ZEND_ENGINE_2
2316 +static void my_destroy_property_info(zend_property_info*, apc_free_t);
2317 +static void my_destroy_arg_info_array(zend_arg_info* src, uint, apc_free_t);
2318 +static void my_destroy_arg_info(zend_arg_info*, apc_free_t);
2319 +#endif
2320 +
2321 +/*
2322 + * The "free" functions work exactly like their "destroy" counterparts (see
2323 + * above) but also free the pointer to the data structure.
2324 + */
2325 +static void my_free_zval_ptr(zval**, apc_free_t);
2326 +static void my_free_function(zend_function*, apc_free_t);
2327 +static void my_free_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
2328 +#ifdef ZEND_ENGINE_2
2329 +static void my_free_property_info(zend_property_info* src, apc_free_t);
2330 +static void my_free_arg_info_array(zend_arg_info*, uint, apc_free_t);
2331 +static void my_free_arg_info(zend_arg_info*, apc_free_t);
2332 +#endif
2333 +
2334 +/*
2335 + * The "fixup" functions need for ZEND_ENGINE_2
2336 + */
2337 +#ifdef ZEND_ENGINE_2
2338 +static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
2339 +static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst );
2340 +/* my_fixup_function_for_execution is the same as my_fixup_function
2341 + * but named differently for clarity
2342 + */
2343 +#define my_fixup_function_for_execution my_fixup_function
2344 +
2345 +#ifdef ZEND_ENGINE_2_2
2346 +static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
2347 +#define my_fixup_property_info_for_execution my_fixup_property_info
2348 +#endif
2349 +
2350 +#endif
2351 +
2352 +/*
2353 + * These functions return "1" if the member/function is
2354 + * defined/overridden in the 'current' class and not inherited.
2355 + */
2356 +static int my_check_copy_function(Bucket* src, va_list args);
2357 +static int my_check_copy_default_property(Bucket* p, va_list args);
2358 +#ifdef ZEND_ENGINE_2
2359 +static int my_check_copy_property_info(Bucket* src, va_list args);
2360 +static int my_check_copy_static_member(Bucket* src, va_list args);
2361 +#endif
2362 +
2363 +/* }}} */
2364 +
2365 +/* {{{ check_op_array_integrity */
2366 +#if 0
2367 +static void check_op_array_integrity(zend_op_array* src)
2368 +{
2369 + int i, j;
2370 +
2371 + /* These sorts of checks really aren't particularly effective, but they
2372 + * can provide a welcome sanity check when debugging. Just don't enable
2373 + * for production use! */
2374 +
2375 + assert(src->refcount != NULL);
2376 + assert(src->opcodes != NULL);
2377 + assert(src->last > 0);
2378 +
2379 + for (i = 0; i < src->last; i++) {
2380 + zend_op* op = &src->opcodes[i];
2381 + znode* nodes[] = { &op->result, &op->op1, &op->op2 };
2382 + for (j = 0; j < 3; j++) {
2383 + assert(nodes[j]->op_type == IS_CONST ||
2384 + nodes[j]->op_type == IS_VAR ||
2385 + nodes[j]->op_type == IS_TMP_VAR ||
2386 + nodes[j]->op_type == IS_UNUSED);
2387 +
2388 + if (nodes[j]->op_type == IS_CONST) {
2389 + int type = nodes[j]->u.constant.type;
2390 + assert(type == IS_RESOURCE ||
2391 + type == IS_BOOL ||
2392 + type == IS_LONG ||
2393 + type == IS_DOUBLE ||
2394 + type == IS_NULL ||
2395 + type == IS_CONSTANT ||
2396 + type == IS_STRING ||
2397 + type == FLAG_IS_BC ||
2398 + type == IS_ARRAY ||
2399 + type == IS_CONSTANT_ARRAY ||
2400 + type == IS_OBJECT);
2401 + }
2402 + }
2403 + }
2404 +}
2405 +#endif
2406 +/* }}} */
2407 +
2408 +/* {{{ is_derived_class */
2409 +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size)
2410 +{
2411 + int i;
2412 +
2413 + /*
2414 + * Scan the op_array for execution-time class declarations of derived
2415 + * classes. If we find one whose key matches our current class key, we
2416 + * know the current class is a derived class.
2417 + *
2418 + * This check is exceedingly inefficient (fortunately it only has to occur
2419 + * once, when the source file is first compiled and cached), but the
2420 + * compiler should save this information for us -- definitely a candidate
2421 + * for a Zend Engine patch.
2422 + *
2423 + * XXX checking for derived classes provides a minimal (albeit measurable)
2424 + * speed up. It may not be worth the added complexity -- considere
2425 + * removing this optimization.
2426 + */
2427 +
2428 + for (i = 0; i < op_array->last; i++) {
2429 + zend_op* op = &op_array->opcodes[i];
2430 +
2431 +#ifdef ZEND_ENGINE_2
2432 + if (op->opcode == ZEND_DECLARE_CLASS &&
2433 + op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
2434 +#else
2435 + if (op->opcode == ZEND_DECLARE_FUNCTION_OR_CLASS &&
2436 + op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
2437 +#endif
2438 + {
2439 + if (op->op1.u.constant.value.str.len == key_size &&
2440 + !memcmp(op->op1.u.constant.value.str.val, key, key_size))
2441 + {
2442 + return 1;
2443 + }
2444 + }
2445 + }
2446 +
2447 + return 0;
2448 +}
2449 +/* }}} */
2450 +
2451 +/* {{{ my_bitwise_copy_function */
2452 +static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate)
2453 +{
2454 + assert(src != NULL);
2455 +
2456 + if (!dst) {
2457 + CHECK(dst = (zend_function*) allocate(sizeof(src[0])));
2458 + }
2459 +
2460 + /* We only need to do a bitwise copy */
2461 + memcpy(dst, src, sizeof(src[0]));
2462 +
2463 + return dst;
2464 +}
2465 +/* }}} */
2466 +
2467 +/* {{{ my_copy_zval_ptr */
2468 +static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_malloc_t allocate, apc_free_t deallocate)
2469 +{
2470 + int local_dst_alloc = 0;
2471 + zval* dst_new;
2472 +
2473 + assert(src != NULL);
2474 +
2475 + if (!dst) {
2476 + CHECK(dst = (zval**) allocate(sizeof(zval*)));
2477 + local_dst_alloc = 1;
2478 + }
2479 +
2480 + if(!(dst[0] = (zval*) allocate(sizeof(zval)))) {
2481 + if(local_dst_alloc) deallocate(dst);
2482 + return NULL;
2483 + }
2484 + dst_new = my_copy_zval(*dst, *src, allocate, deallocate);
2485 + if(dst_new != *dst) {
2486 + deallocate(*dst);
2487 + *dst = dst_new;
2488 + }
2489 +
2490 + (*dst)->refcount = (*src)->refcount;
2491 + (*dst)->is_ref = (*src)->is_ref;
2492 +
2493 + return dst;
2494 +}
2495 +/* }}} */
2496 +
2497 +/* {{{ my_copy_zval */
2498 +static zval* my_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
2499 +{
2500 + zval **tmp;
2501 + TSRMLS_FETCH();
2502 +
2503 + assert(dst != NULL);
2504 + assert(src != NULL);
2505 +
2506 + memcpy(dst, src, sizeof(src[0]));
2507 +
2508 + switch (src->type & ~IS_CONSTANT_INDEX) {
2509 + case IS_RESOURCE:
2510 + case IS_BOOL:
2511 + case IS_LONG:
2512 + case IS_DOUBLE:
2513 + case IS_NULL:
2514 + break;
2515 +
2516 + case IS_CONSTANT:
2517 + case IS_STRING:
2518 +#ifndef ZEND_ENGINE_2
2519 + case FLAG_IS_BC:
2520 +#endif
2521 + if (src->value.str.val) {
2522 + CHECK(dst->value.str.val = apc_xmemcpy(src->value.str.val,
2523 + src->value.str.len+1,
2524 + allocate));
2525 + }
2526 + break;
2527 +
2528 + case IS_ARRAY:
2529 +
2530 + if(APCG(copied_zvals)) {
2531 + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
2532 + (*tmp)->refcount++;
2533 + return *tmp;
2534 + }
2535 +
2536 + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL);
2537 + }
2538 + /* fall through */
2539 +
2540 + case IS_CONSTANT_ARRAY:
2541 +
2542 + CHECK(dst->value.ht =
2543 + my_copy_hashtable(NULL,
2544 + src->value.ht,
2545 + (ht_copy_fun_t) my_copy_zval_ptr,
2546 + (ht_free_fun_t) my_free_zval_ptr,
2547 + 1,
2548 + allocate, deallocate));
2549 + break;
2550 +
2551 + case IS_OBJECT:
2552 +#ifndef ZEND_ENGINE_2
2553 + CHECK(dst->value.obj.ce =
2554 + my_copy_class_entry(NULL, src->value.obj.ce, allocate, deallocate));
2555 +
2556 + if(!(dst->value.obj.properties = my_copy_hashtable(NULL,
2557 + src->value.obj.properties,
2558 + (ht_copy_fun_t) my_copy_zval_ptr,
2559 + (ht_free_fun_t) my_free_zval_ptr,
2560 + 1,
2561 + allocate, deallocate))) {
2562 + my_destroy_class_entry(dst->value.obj.ce, deallocate);
2563 + return NULL;
2564 + }
2565 + break;
2566 +#else
2567 + dst->type = IS_NULL;
2568 +#endif
2569 + break;
2570 +
2571 + default:
2572 + assert(0);
2573 + }
2574 +
2575 + return dst;
2576 +}
2577 +/* }}} */
2578 +
2579 +/* {{{ my_copy_znode */
2580 +static znode* my_copy_znode(znode* dst, znode* src, apc_malloc_t allocate, apc_free_t deallocate)
2581 +{
2582 + assert(dst != NULL);
2583 + assert(src != NULL);
2584 +
2585 + memcpy(dst, src, sizeof(src[0]));
2586 +
2587 +#ifdef IS_CV
2588 + assert(dst ->op_type == IS_CONST ||
2589 + dst ->op_type == IS_VAR ||
2590 + dst ->op_type == IS_CV ||
2591 + dst ->op_type == IS_TMP_VAR ||
2592 + dst ->op_type == IS_UNUSED);
2593 +#else
2594 + assert(dst ->op_type == IS_CONST ||
2595 + dst ->op_type == IS_VAR ||
2596 + dst ->op_type == IS_TMP_VAR ||
2597 + dst ->op_type == IS_UNUSED);
2598 +#endif
2599 +
2600 + if (src->op_type == IS_CONST) {
2601 + if(!my_copy_zval(&dst->u.constant, &src->u.constant, allocate, deallocate)) {
2602 + return NULL;
2603 + }
2604 + }
2605 +
2606 + return dst;
2607 +}
2608 +/* }}} */
2609 +
2610 +/* {{{ my_copy_zend_op */
2611 +static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_malloc_t allocate, apc_free_t deallocate)
2612 +{
2613 + assert(dst != NULL);
2614 + assert(src != NULL);
2615 +
2616 + memcpy(dst, src, sizeof(src[0]));
2617 +
2618 + if( my_copy_znode(&dst->result, &src->result, allocate, deallocate) == NULL
2619 + || my_copy_znode(&dst->op1, &src->op1, allocate, deallocate) == NULL
2620 + || my_copy_znode(&dst->op2, &src->op2, allocate, deallocate) == NULL)
2621 + {
2622 + return NULL;
2623 + }
2624 +
2625 + return dst;
2626 +}
2627 +/* }}} */
2628 +
2629 +/* {{{ my_copy_function */
2630 +static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
2631 +{
2632 + int local_dst_alloc = 0;
2633 + TSRMLS_FETCH();
2634 +
2635 + assert(src != NULL);
2636 +
2637 + if(!dst) local_dst_alloc = 1;
2638 + CHECK(dst = my_bitwise_copy_function(dst, src, allocate));
2639 +
2640 + switch (src->type) {
2641 + case ZEND_INTERNAL_FUNCTION:
2642 + case ZEND_OVERLOADED_FUNCTION:
2643 + /* shallow copy because op_array is internal */
2644 + dst->op_array = src->op_array;
2645 + break;
2646 +
2647 + case ZEND_USER_FUNCTION:
2648 + case ZEND_EVAL_CODE:
2649 + if(!apc_copy_op_array(&dst->op_array,
2650 + &src->op_array,
2651 + allocate, deallocate TSRMLS_CC)) {
2652 + if(local_dst_alloc) deallocate(dst);
2653 + return NULL;
2654 + }
2655 + break;
2656 +
2657 + default:
2658 + assert(0);
2659 + }
2660 +#ifdef ZEND_ENGINE_2
2661 + /*
2662 + * op_array bitwise copying overwrites what ever you modified
2663 + * before apc_copy_op_array - which is why this code is outside
2664 + * my_bitwise_copy_function.
2665 + */
2666 +
2667 + /* zend_do_inheritance will re-look this up, because the pointers
2668 + * in prototype are from a function table of another class. It just
2669 + * helps if that one is from EG(class_table).
2670 + */
2671 + dst->common.prototype = NULL;
2672 +
2673 + /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you
2674 + * have to carry around a prototype. Thankfully zend_do_inheritance
2675 + * sets this properly as well
2676 + */
2677 + dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT);
2678 +#endif
2679 +
2680 +
2681 + return dst;
2682 +}
2683 +/* }}} */
2684 +
2685 +/* {{{ my_copy_function_entry */
2686 +static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, zend_function_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
2687 +{
2688 + int local_dst_alloc = 0;
2689 + assert(src != NULL);
2690 +
2691 + if (!dst) {
2692 + CHECK(dst = (zend_function_entry*) allocate(sizeof(src[0])));
2693 + local_dst_alloc = 1;
2694 + }
2695 +
2696 + /* Start with a bitwise copy */
2697 + memcpy(dst, src, sizeof(src[0]));
2698 +
2699 + dst->fname = NULL;
2700 +#ifdef ZEND_ENGINE_2
2701 + dst->arg_info = NULL;
2702 +#else
2703 + dst->func_arg_types = NULL;
2704 +#endif
2705 +
2706 + if (src->fname) {
2707 + if(!(dst->fname = apc_xstrdup(src->fname, allocate))) {
2708 + goto cleanup;
2709 + }
2710 + }
2711 +
2712 +#ifdef ZEND_ENGINE_2
2713 + if (src->arg_info) {
2714 + if(!(dst->arg_info = my_copy_arg_info_array(NULL,
2715 + src->arg_info,
2716 + src->num_args,
2717 + allocate,
2718 + deallocate))) {
2719 + goto cleanup;
2720 + }
2721 + }
2722 +#else
2723 + if (src->func_arg_types) {
2724 + if(!(dst->func_arg_types = apc_xmemcpy(src->func_arg_types,
2725 + src->func_arg_types[0]+1,
2726 + allocate))) {
2727 + goto cleanup;
2728 + }
2729 + }
2730 +#endif
2731 +
2732 + return dst;
2733 +
2734 +cleanup:
2735 + if(dst->fname) deallocate(dst->fname);
2736 + if(local_dst_alloc) deallocate(dst);
2737 + return NULL;
2738 +}
2739 +/* }}} */
2740 +
2741 +#ifdef ZEND_ENGINE_2
2742 +/* {{{ my_copy_property_info */
2743 +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
2744 +{
2745 + int local_dst_alloc = 0;
2746 +
2747 + assert(src != NULL);
2748 +
2749 + if (!dst) {
2750 + CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
2751 + local_dst_alloc = 1;
2752 + }
2753 +
2754 + /* Start with a bitwise copy */
2755 + memcpy(dst, src, sizeof(*src));
2756 +
2757 + dst->name = NULL;
2758 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2759 + dst->doc_comment = NULL;
2760 +#endif
2761 +
2762 + if (src->name) {
2763 + /* private members are stored inside property_info as a mangled
2764 + * string of the form:
2765 + * \0<classname>\0<membername>\0
2766 + */
2767 + if(!(dst->name =
2768 + apc_xmemcpy(src->name, src->name_length+1, allocate))) {
2769 + goto cleanup;
2770 + }
2771 + }
2772 +
2773 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2774 + if (src->doc_comment) {
2775 + if( !(dst->doc_comment =
2776 + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
2777 + goto cleanup;
2778 + }
2779 + }
2780 +#endif
2781 +
2782 + return dst;
2783 +
2784 +cleanup:
2785 + if(dst->name) deallocate(dst->name);
2786 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2787 + if(dst->doc_comment) deallocate(dst->doc_comment);
2788 +#endif
2789 + if(local_dst_alloc) deallocate(dst);
2790 + return NULL;
2791 +}
2792 +/* }}} */
2793 +
2794 +/* {{{ my_copy_property_info_for_execution */
2795 +static zend_property_info* my_copy_property_info_for_execution(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
2796 +{
2797 + int local_dst_alloc = 0;
2798 +
2799 + assert(src != NULL);
2800 +
2801 + if (!dst) {
2802 + CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
2803 + local_dst_alloc = 1;
2804 + }
2805 +
2806 + /* We need only a shallow copy */
2807 + memcpy(dst, src, sizeof(*src));
2808 +
2809 + return dst;
2810 +}
2811 +/* }}} */
2812 +
2813 +/* {{{ my_copy_arg_info_array */
2814 +static zend_arg_info* my_copy_arg_info_array(zend_arg_info* dst, zend_arg_info* src, uint num_args, apc_malloc_t allocate, apc_free_t deallocate)
2815 +{
2816 + int local_dst_alloc = 0;
2817 + int i = 0;
2818 +
2819 +
2820 + if (!dst) {
2821 + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)*num_args));
2822 + local_dst_alloc = 1;
2823 + }
2824 +
2825 + /* Start with a bitwise copy */
2826 + memcpy(dst, src, sizeof(*src)*num_args);
2827 +
2828 + for(i=0; i < num_args; i++) {
2829 + if(!(my_copy_arg_info( &dst[i], &src[i], allocate, deallocate))) {
2830 + if(i) my_destroy_arg_info_array(dst, i-1, deallocate);
2831 + if(local_dst_alloc) deallocate(dst);
2832 + return NULL;
2833 + }
2834 + }
2835 +
2836 + return dst;
2837 +}
2838 +/* }}} */
2839 +
2840 +/* {{{ my_copy_arg_info */
2841 +static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, zend_arg_info* src, apc_malloc_t allocate, apc_free_t deallocate)
2842 +{
2843 + int local_dst_alloc = 0;
2844 +
2845 + assert(src != NULL);
2846 +
2847 + if (!dst) {
2848 + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)));
2849 + local_dst_alloc = 1;
2850 + }
2851 +
2852 + /* Start with a bitwise copy */
2853 + memcpy(dst, src, sizeof(*src));
2854 +
2855 + dst->name = NULL;
2856 + dst->class_name = NULL;
2857 +
2858 + if (src->name) {
2859 + if(!(dst->name =
2860 + apc_xmemcpy(src->name, src->name_len+1, allocate))) {
2861 + goto cleanup;
2862 + }
2863 + }
2864 +
2865 + if (src->class_name) {
2866 + if(!(dst->class_name =
2867 + apc_xmemcpy(src->class_name, src->class_name_len+1, allocate))) {
2868 + goto cleanup;
2869 + }
2870 + }
2871 +
2872 + return dst;
2873 +
2874 +cleanup:
2875 + if(dst->name) deallocate(dst->name);
2876 + if(dst->class_name) deallocate(dst->name);
2877 + if(local_dst_alloc) deallocate(dst);
2878 + return NULL;
2879 +}
2880 +/* }}} */
2881 +#endif
2882 +
2883 +/* {{{ my_copy_class_entry */
2884 +static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
2885 +{
2886 + int local_dst_alloc = 0;
2887 + int i = 0;
2888 +
2889 + assert(src != NULL);
2890 +
2891 + if (!dst) {
2892 + CHECK(dst = (zend_class_entry*) allocate(sizeof(*src)));
2893 + local_dst_alloc = 1;
2894 + }
2895 +
2896 + /* Start with a bitwise copy */
2897 + memcpy(dst, src, sizeof(*src));
2898 +
2899 + dst->name = NULL;
2900 + dst->builtin_functions = NULL;
2901 + memset(&dst->function_table, 0, sizeof(dst->function_table));
2902 + memset(&dst->default_properties, 0, sizeof(dst->default_properties));
2903 +#ifndef ZEND_ENGINE_2
2904 + dst->refcount = NULL;
2905 +#else
2906 + dst->static_members = NULL;
2907 + dst->doc_comment = NULL;
2908 + dst->filename = NULL;
2909 + memset(&dst->properties_info, 0, sizeof(dst->properties_info));
2910 + memset(&dst->constants_table, 0, sizeof(dst->constants_table));
2911 + memset(&dst->default_static_members, 0, sizeof(dst->default_static_members));
2912 +#endif
2913 +
2914 + if (src->name) {
2915 + if(!(dst->name = apc_xstrdup(src->name, allocate))) {
2916 + goto cleanup;
2917 + }
2918 + }
2919 +
2920 +#ifndef ZEND_ENGINE_2
2921 + if(!(dst->refcount = apc_xmemcpy(src->refcount,
2922 + sizeof(src->refcount[0]),
2923 + allocate))) {
2924 + goto cleanup;
2925 + }
2926 +#endif
2927 +
2928 + if(!(my_copy_hashtable_ex(&dst->function_table,
2929 + &src->function_table,
2930 + (ht_copy_fun_t) my_copy_function,
2931 + (ht_free_fun_t) my_free_function,
2932 + 0,
2933 + allocate, deallocate,
2934 + (ht_check_copy_fun_t) my_check_copy_function,
2935 + src))) {
2936 + goto cleanup;
2937 + }
2938 +
2939 +#ifdef ZEND_ENGINE_2
2940 +
2941 + /* the interfaces are populated at runtime using ADD_INTERFACE */
2942 + dst->interfaces = NULL;
2943 +
2944 + /* the current count includes inherited interfaces as well,
2945 + the real dynamic ones are the first <n> which are zero'd
2946 + out in zend_do_end_class_declaration */
2947 + for(i = 0 ; i < src->num_interfaces ; i++) {
2948 + if(src->interfaces[i])
2949 + {
2950 + dst->num_interfaces = i;
2951 + break;
2952 + }
2953 + }
2954 +
2955 + /* these will either be set inside my_fixup_hashtable or
2956 + * they will be copied out from parent inside zend_do_inheritance
2957 + */
2958 + dst->constructor = NULL;
2959 + dst->destructor = NULL;
2960 + dst->clone = NULL;
2961 + dst->__get = NULL;
2962 + dst->__set = NULL;
2963 + dst->__unset = NULL;
2964 + dst->__isset = NULL;
2965 + dst->__call = NULL;
2966 +#ifdef ZEND_ENGINE_2_2
2967 + dst->__tostring = NULL;
2968 +#endif
2969 +
2970 + /* unset function proxies */
2971 + dst->serialize_func = NULL;
2972 + dst->unserialize_func = NULL;
2973 +
2974 + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst);
2975 +#endif
2976 +
2977 + if(!(my_copy_hashtable_ex(&dst->default_properties,
2978 + &src->default_properties,
2979 + (ht_copy_fun_t) my_copy_zval_ptr,
2980 + (ht_free_fun_t) my_free_zval_ptr,
2981 + 1,
2982 + allocate,deallocate,
2983 + (ht_check_copy_fun_t) my_check_copy_default_property,
2984 + src))) {
2985 + goto cleanup;
2986 + }
2987 +
2988 +#ifdef ZEND_ENGINE_2
2989 +
2990 + if(!(my_copy_hashtable_ex(&dst->properties_info,
2991 + &src->properties_info,
2992 + (ht_copy_fun_t) my_copy_property_info,
2993 + (ht_free_fun_t) my_free_property_info,
2994 + 0,
2995 + allocate, deallocate,
2996 + (ht_check_copy_fun_t) my_check_copy_property_info,
2997 + src))) {
2998 + goto cleanup;
2999 + }
3000 +
3001 +#ifdef ZEND_ENGINE_2_2
3002 + /* php5.2 introduced a scope attribute for property info */
3003 + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
3004 +#endif
3005 +
3006 + if(!my_copy_hashtable_ex(&dst->default_static_members,
3007 + &src->default_static_members,
3008 + (ht_copy_fun_t) my_copy_zval_ptr,
3009 + (ht_free_fun_t) my_free_zval_ptr,
3010 + 1,
3011 + allocate, deallocate,
3012 + (ht_check_copy_fun_t) my_check_copy_static_member,
3013 + src,
3014 + &src->default_static_members)) {
3015 + goto cleanup;
3016 + }
3017 + if(src->static_members != &src->default_static_members)
3018 + {
3019 + if(!(dst->static_members = my_copy_hashtable_ex(NULL,
3020 + src->static_members,
3021 + (ht_copy_fun_t) my_copy_zval_ptr,
3022 + (ht_free_fun_t) my_free_zval_ptr,
3023 + 1,
3024 + allocate, deallocate,
3025 + (ht_check_copy_fun_t) my_check_copy_static_member,
3026 + src,
3027 + src->static_members))) {
3028 + goto cleanup;
3029 + }
3030 + }
3031 + else
3032 + {
3033 + dst->static_members = &dst->default_static_members;
3034 + }
3035 +
3036 + if(!(my_copy_hashtable(&dst->constants_table,
3037 + &src->constants_table,
3038 + (ht_copy_fun_t) my_copy_zval_ptr,
3039 + (ht_free_fun_t) my_free_zval_ptr,
3040 + 1,
3041 + allocate, deallocate))) {
3042 + goto cleanup;
3043 + }
3044 +
3045 + if (src->doc_comment) {
3046 + if(!(dst->doc_comment =
3047 + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
3048 + goto cleanup;
3049 + }
3050 + }
3051 +#endif
3052 +
3053 + if (src->builtin_functions) {
3054 + int i, n;
3055 +
3056 + for (n = 0; src->type == ZEND_INTERNAL_CLASS && src->builtin_functions[n].fname != NULL; n++) {}
3057 +
3058 + if(!(dst->builtin_functions =
3059 + (zend_function_entry*)
3060 + allocate((n + 1) * sizeof(zend_function_entry)))) {
3061 + goto cleanup;
3062 + }
3063 +
3064 +
3065 + for (i = 0; i < n; i++) {
3066 + if(!my_copy_function_entry(&dst->builtin_functions[i],
3067 + &src->builtin_functions[i],
3068 + allocate, deallocate)) {
3069 + int ii;
3070 +
3071 + for(ii=i-1; i>=0; i--) my_destroy_function_entry(&dst->builtin_functions[ii], deallocate);
3072 + goto cleanup;
3073 + }
3074 + }
3075 + dst->builtin_functions[n].fname = NULL;
3076 + }
3077 +
3078 +#ifdef ZEND_ENGINE_2
3079 + if (src->filename) {
3080 + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
3081 + goto cleanup;
3082 + }
3083 + }
3084 +#endif
3085 +
3086 + return dst;
3087 +
3088 +
3089 +cleanup:
3090 + if(dst->name) deallocate(dst->name);
3091 +#ifdef ZEND_ENGINE_2
3092 + if(dst->doc_comment) deallocate(dst->doc_comment);
3093 + if(dst->filename) deallocate(dst->filename);
3094 +#else
3095 + if(dst->refcount) deallocate(dst->refcount);
3096 +#endif
3097 +
3098 + if(dst->builtin_functions) deallocate(dst->builtin_functions);
3099 + if(dst->function_table.arBuckets) my_destroy_hashtable(&dst->function_table, (ht_free_fun_t) my_free_function, deallocate);
3100 + if(dst->default_properties.arBuckets) my_destroy_hashtable(&dst->default_properties, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3101 +
3102 +#ifdef ZEND_ENGINE_2
3103 + if(dst->properties_info.arBuckets) my_destroy_hashtable(&dst->properties_info, (ht_free_fun_t) my_free_property_info, deallocate);
3104 + if(dst->default_static_members.arBuckets)
3105 + {
3106 + my_destroy_hashtable(&dst->default_static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3107 + }
3108 + if(dst->static_members && dst->static_members != &(dst->default_static_members))
3109 + {
3110 + my_destroy_hashtable(dst->static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3111 + deallocate(dst->static_members);
3112 + }
3113 + if(dst->constants_table.arBuckets) my_destroy_hashtable(&dst->constants_table, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3114 +#endif
3115 + if(local_dst_alloc) deallocate(dst);
3116 +
3117 + return NULL;
3118 +}
3119 +/* }}} */
3120 +
3121 +/* {{{ my_copy_hashtable */
3122 +static HashTable* my_copy_hashtable_ex(HashTable* dst,
3123 + HashTable* src,
3124 + ht_copy_fun_t copy_fn,
3125 + ht_free_fun_t free_fn,
3126 + int holds_ptrs,
3127 + apc_malloc_t allocate,
3128 + apc_free_t deallocate,
3129 + ht_check_copy_fun_t check_fn,
3130 + ...)
3131 +{
3132 + Bucket* curr = NULL;
3133 + Bucket* prev = NULL;
3134 + Bucket* newp = NULL;
3135 + int first = 1;
3136 + int local_dst_alloc = 0;
3137 + int index = 0;
3138 +
3139 + assert(src != NULL);
3140 +
3141 + if (!dst) {
3142 + CHECK(dst = (HashTable*) allocate(sizeof(src[0])));
3143 + local_dst_alloc = 1;
3144 + }
3145 +
3146 + memcpy(dst, src, sizeof(src[0]));
3147 +
3148 + /* allocate buckets for the new hashtable */
3149 + if(!(dst->arBuckets = allocate(dst->nTableSize * sizeof(Bucket*)))) {
3150 + if(local_dst_alloc) deallocate(dst);
3151 + return NULL;
3152 + }
3153 +
3154 + memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
3155 + dst->pInternalPointer = NULL;
3156 + dst->pListHead = NULL;
3157 +
3158 + for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
3159 + int n = curr->h % dst->nTableSize;
3160 +
3161 + if(check_fn) {
3162 + va_list args;
3163 + va_start(args, check_fn);
3164 +
3165 + /* Call the check_fn to see if the current bucket
3166 + * needs to be copied out
3167 + */
3168 + if(!check_fn(curr, args)) {
3169 + dst->nNumOfElements--;
3170 + continue;
3171 + }
3172 +
3173 + va_end(args);
3174 + }
3175 +
3176 + /* create a copy of the bucket 'curr' */
3177 + if(!(newp =
3178 + (Bucket*) apc_xmemcpy(curr,
3179 + sizeof(Bucket) + curr->nKeyLength - 1,
3180 + allocate))) {
3181 + goto cleanup;
3182 + }
3183 +
3184 + /* insert 'newp' into the linked list at its hashed index */
3185 + if (dst->arBuckets[n]) {
3186 + newp->pNext = dst->arBuckets[n];
3187 + newp->pLast = NULL;
3188 + newp->pNext->pLast = newp;
3189 + }
3190 + else {
3191 + newp->pNext = newp->pLast = NULL;
3192 + }
3193 +
3194 + dst->arBuckets[n] = newp;
3195 +
3196 + /* copy the bucket data using our 'copy_fn' callback function */
3197 + if(!(newp->pData = copy_fn(NULL, curr->pData, allocate, deallocate))) {
3198 + goto cleanup;
3199 + }
3200 +
3201 + if (holds_ptrs) {
3202 + memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
3203 + }
3204 + else {
3205 + newp->pDataPtr = NULL;
3206 + }
3207 +
3208 + /* insert 'newp' into the table-thread linked list */
3209 + newp->pListLast = prev;
3210 + newp->pListNext = NULL;
3211 +
3212 + if (prev) {
3213 + prev->pListNext = newp;
3214 + }
3215 +
3216 + if (first) {
3217 + dst->pListHead = newp;
3218 + first = 0;
3219 + }
3220 +
3221 + prev = newp;
3222 + }
3223 +
3224 + dst->pListTail = newp;
3225 +
3226 + return dst;
3227 +
3228 + cleanup:
3229 + for(index = 0; index < dst->nTableSize; index++)
3230 + {
3231 + curr = dst->arBuckets[index];
3232 + while(curr != NULL)
3233 + {
3234 + Bucket * tmp = curr;
3235 + if(curr->pData && free_fn)
3236 + {
3237 + free_fn(curr->pData, deallocate);
3238 + }
3239 + curr = curr->pNext;
3240 + deallocate(tmp);
3241 + }
3242 + }
3243 + deallocate(dst->arBuckets);
3244 + if(local_dst_alloc) deallocate(dst);
3245 + else dst->arBuckets = NULL;
3246 +
3247 + return NULL;
3248 +}
3249 +/* }}} */
3250 +
3251 +/* {{{ my_copy_static_variables */
3252 +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate)
3253 +{
3254 + if (src->static_variables == NULL) {
3255 + return NULL;
3256 + }
3257 +
3258 + return my_copy_hashtable(NULL,
3259 + src->static_variables,
3260 + (ht_copy_fun_t) my_copy_zval_ptr,
3261 + (ht_free_fun_t) my_free_zval_ptr,
3262 + 1,
3263 + allocate, deallocate);
3264 +}
3265 +/* }}} */
3266 +
3267 +/* {{{ apc_copy_zval */
3268 +zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
3269 +{
3270 + int local_dst_alloc = 0;
3271 + assert(src != NULL);
3272 +
3273 + if (!dst) {
3274 + CHECK(dst = (zval*) allocate(sizeof(zval)));
3275 + local_dst_alloc = 1;
3276 + }
3277 +
3278 + dst = my_copy_zval(dst, src, allocate, deallocate);
3279 + if(!dst) {
3280 + if(local_dst_alloc) deallocate(dst);
3281 + return NULL;
3282 + }
3283 + return dst;
3284 +}
3285 +/* }}} */
3286 +
3287 +#ifdef ZEND_ENGINE_2
3288 +/* {{{ apc_fixup_op_array_jumps */
3289 +static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src )
3290 +{
3291 + int i;
3292 +
3293 + for (i=0; i < dst->last; ++i) {
3294 + zend_op *zo = &(dst->opcodes[i]);
3295 + /*convert opline number to jump address*/
3296 + switch (zo->opcode) {
3297 + case ZEND_JMP:
3298 + /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/
3299 + zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes);
3300 + break;
3301 + case ZEND_JMPZ:
3302 + case ZEND_JMPNZ:
3303 + case ZEND_JMPZ_EX:
3304 + case ZEND_JMPNZ_EX:
3305 + zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes);
3306 + break;
3307 + default:
3308 + break;
3309 + }
3310 + }
3311 +}
3312 +/* }}} */
3313 +#endif
3314 +
3315 +/* {{{ apc_copy_op_array */
3316 +zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
3317 +{
3318 + int i;
3319 + int local_dst_alloc = 0;
3320 + apc_fileinfo_t fileinfo;
3321 + char canon_path[MAXPATHLEN];
3322 + char *fullpath = NULL;
3323 +#ifdef ZEND_ENGINE_2
3324 + apc_opflags_t * flags = NULL;
3325 +#endif
3326 +
3327 + assert(src != NULL);
3328 +
3329 + if (!dst) {
3330 + CHECK(dst = (zend_op_array*) allocate(sizeof(src[0])));
3331 + local_dst_alloc = 1;
3332 + }
3333 +
3334 + if(APCG(apc_optimize_function)) {
3335 + APCG(apc_optimize_function)(src TSRMLS_CC);
3336 + }
3337 +
3338 + /* start with a bitwise copy of the array */
3339 + memcpy(dst, src, sizeof(src[0]));
3340 +
3341 + dst->function_name = NULL;
3342 + dst->filename = NULL;
3343 + dst->refcount = NULL;
3344 + dst->opcodes = NULL;
3345 + dst->brk_cont_array = NULL;
3346 + dst->static_variables = NULL;
3347 +#ifdef ZEND_ENGINE_2
3348 + dst->try_catch_array = NULL;
3349 + dst->arg_info = NULL;
3350 + dst->doc_comment = NULL;
3351 +#else
3352 + dst->arg_types = NULL;
3353 +#endif
3354 +#ifdef ZEND_ENGINE_2_1
3355 + dst->vars = NULL;
3356 +#endif
3357 +
3358 + /* copy the arg types array (if set) */
3359 +#ifdef ZEND_ENGINE_2
3360 + if (src->arg_info) {
3361 + if(!(dst->arg_info = my_copy_arg_info_array(NULL,
3362 + src->arg_info,
3363 + src->num_args,
3364 + allocate,
3365 + deallocate))) {
3366 + goto cleanup;
3367 + }
3368 + }
3369 +#else
3370 + if (src->arg_types) {
3371 + if(!(dst->arg_types = apc_xmemcpy(src->arg_types,
3372 + sizeof(src->arg_types[0]) * (src->arg_types[0]+1),
3373 + allocate))) {
3374 + goto cleanup;
3375 + }
3376 + }
3377 +#endif
3378 +
3379 + if (src->function_name) {
3380 + if(!(dst->function_name = apc_xstrdup(src->function_name, allocate))) {
3381 + goto cleanup;
3382 + }
3383 + }
3384 + if (src->filename) {
3385 + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
3386 + goto cleanup;
3387 + }
3388 + }
3389 +
3390 + if(!(dst->refcount = apc_xmemcpy(src->refcount,
3391 + sizeof(src->refcount[0]),
3392 + allocate))) {
3393 + goto cleanup;
3394 + }
3395 +
3396 + /* deep-copy the opcodes */
3397 + if(!(dst->opcodes = (zend_op*) allocate(sizeof(zend_op) * src->last))) {
3398 + goto cleanup;
3399 + }
3400 +
3401 +#ifdef ZEND_ENGINE_2
3402 + if(APCG(reserved_offset) != -1) {
3403 + /* Insanity alert: the void* pointer is cast into an apc_opflags_t
3404 + * struct. apc_zend_init() checks to ensure that it fits in a void* */
3405 + flags = (apc_opflags_t*) & (dst->reserved[APCG(reserved_offset)]);
3406 + memset(flags, 0, sizeof(apc_opflags_t));
3407 + /* assert(sizeof(apc_opflags_t) < sizeof(dst->reserved)); */
3408 + }
3409 +#endif
3410 +
3411 + for (i = 0; i < src->last; i++) {
3412 +#ifdef ZEND_ENGINE_2
3413 + zend_op *zo = &(src->opcodes[i]);
3414 + /* a lot of files are merely constant arrays with no jumps */
3415 + switch (zo->opcode) {
3416 + case ZEND_JMP:
3417 + case ZEND_JMPZ:
3418 + case ZEND_JMPNZ:
3419 + case ZEND_JMPZ_EX:
3420 + case ZEND_JMPNZ_EX:
3421 + if(flags != NULL) {
3422 + flags->has_jumps = 1;
3423 + }
3424 + break;
3425 +#ifdef ZEND_ENGINE_2
3426 + /* auto_globals_jit was not in php-4.3.* */
3427 + case ZEND_FETCH_R:
3428 + case ZEND_FETCH_W:
3429 + case ZEND_FETCH_IS:
3430 + case ZEND_FETCH_FUNC_ARG:
3431 + if(PG(auto_globals_jit) && flags != NULL)
3432 + {
3433 + /* The fetch is only required if auto_globals_jit=1 */
3434 + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL &&
3435 + zo->op1.op_type == IS_CONST &&
3436 + zo->op1.u.constant.type == IS_STRING) {
3437 + znode * varname = &zo->op1;
3438 + if (varname->u.constant.value.str.val[0] == '_') {
3439 +#define SET_IF_AUTOGLOBAL(member) \
3440 + if(!strcmp(varname->u.constant.value.str.val, #member)) \
3441 + flags->member = 1 /* no ';' here */
3442 + SET_IF_AUTOGLOBAL(_GET);
3443 + else SET_IF_AUTOGLOBAL(_POST);
3444 + else SET_IF_AUTOGLOBAL(_COOKIE);
3445 + else SET_IF_AUTOGLOBAL(_SERVER);
3446 + else SET_IF_AUTOGLOBAL(_ENV);
3447 + else SET_IF_AUTOGLOBAL(_FILES);
3448 + else SET_IF_AUTOGLOBAL(_REQUEST);
3449 + else if(zend_is_auto_global(
3450 + varname->u.constant.value.str.val,
3451 + varname->u.constant.value.str.len
3452 + TSRMLS_CC))
3453 + {
3454 + flags->unknown_global = 1;
3455 + }
3456 + }
3457 + }
3458 + }
3459 + break;
3460 +#endif
3461 + case ZEND_RECV_INIT:
3462 + if(zo->op2.op_type == IS_CONST &&
3463 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY) {
3464 + if(flags != NULL) {
3465 + flags->deep_copy = 1;
3466 + }
3467 + }
3468 + break;
3469 + default:
3470 + if((zo->op1.op_type == IS_CONST &&
3471 + zo->op1.u.constant.type == IS_CONSTANT_ARRAY) ||
3472 + (zo->op2.op_type == IS_CONST &&
3473 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) {
3474 + if(flags != NULL) {
3475 + flags->deep_copy = 1;
3476 + }
3477 + }
3478 + break;
3479 + }
3480 +#endif
3481 + if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, allocate, deallocate))) {
3482 + int ii;
3483 + for(ii = i-1; ii>=0; ii--) {
3484 + my_destroy_zend_op(dst->opcodes+ii, deallocate);
3485 + }
3486 + goto cleanup;
3487 + }
3488 +#ifdef ZEND_ENGINE_2
3489 +/* This code breaks apc's rule#1 - cache what you compile */
3490 + if(APCG(fpstat)==0) {
3491 + if((zo->opcode == ZEND_INCLUDE_OR_EVAL) &&
3492 + (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) {
3493 + /* constant includes */
3494 + if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),Z_STRLEN_P(&zo->op1.u.constant))) {
3495 + if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), &fileinfo) == 0) {
3496 + if((fullpath = realpath(fileinfo.fullpath, canon_path))) {
3497 + /* everything has to go through a realpath() */
3498 + zend_op *dzo = &(dst->opcodes[i]);
3499 + deallocate(dzo->op1.u.constant.value.str.val);
3500 + dzo->op1.u.constant.value.str.len = strlen(fullpath);
3501 + dzo->op1.u.constant.value.str.val = apc_xstrdup(fullpath, allocate);
3502 + }
3503 + }
3504 + }
3505 + }
3506 + }
3507 +#endif
3508 + }
3509 +
3510 +#ifdef ZEND_ENGINE_2
3511 + if(flags == NULL || flags->has_jumps) {
3512 + apc_fixup_op_array_jumps(dst,src);
3513 + }
3514 +#endif
3515 +
3516 + /* copy the break-continue array */
3517 + if (src->brk_cont_array) {
3518 + if(!(dst->brk_cont_array =
3519 + apc_xmemcpy(src->brk_cont_array,
3520 + sizeof(src->brk_cont_array[0]) * src->last_brk_cont,
3521 + allocate))) {
3522 + goto cleanup_opcodes;
3523 + }
3524 + }
3525 +
3526 + /* copy the table of static variables */
3527 + if (src->static_variables) {
3528 + if(!(dst->static_variables = my_copy_static_variables(src, allocate, deallocate))) {
3529 + goto cleanup_opcodes;
3530 + }
3531 + }
3532 +
3533 +#ifdef ZEND_ENGINE_2
3534 + if (src->try_catch_array) {
3535 + if(!(dst->try_catch_array =
3536 + apc_xmemcpy(src->try_catch_array,
3537 + sizeof(src->try_catch_array[0]) * src->last_try_catch,
3538 + allocate))) {
3539 + goto cleanup_opcodes;
3540 + }
3541 + }
3542 +#endif
3543 +
3544 +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */
3545 + if (src->vars) {
3546 + if(!(dst->vars = apc_xmemcpy(src->vars,
3547 + sizeof(src->vars[0]) * src->last_var,
3548 + allocate))) {
3549 + goto cleanup_opcodes;
3550 + }
3551 +
3552 + for(i = 0; i < src->last_var; i++) dst->vars[i].name = NULL;
3553 +
3554 + for(i = 0; i < src->last_var; i++) {
3555 + if(!(dst->vars[i].name = apc_xmemcpy(src->vars[i].name,
3556 + src->vars[i].name_len + 1,
3557 + allocate))) {
3558 + dst->last_var = i;
3559 + goto cleanup_opcodes;
3560 + }
3561 + }
3562 + }
3563 +#endif
3564 +
3565 +#ifdef ZEND_ENGINE_2
3566 + if (src->doc_comment) {
3567 + if (!(dst->doc_comment
3568 + = apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
3569 + goto cleanup_opcodes;
3570 + }
3571 + }
3572 +#endif
3573 +
3574 + return dst;
3575 +
3576 +cleanup_opcodes:
3577 + if(dst->opcodes) {
3578 + for(i=0; i < src->last; i++) my_destroy_zend_op(dst->opcodes+i, deallocate);
3579 + }
3580 +cleanup:
3581 + if(dst->function_name) deallocate(dst->function_name);
3582 + if(dst->refcount) deallocate(dst->refcount);
3583 + if(dst->filename) deallocate(dst->filename);
3584 +#ifdef ZEND_ENGINE_2
3585 + if(dst->arg_info) my_free_arg_info_array(dst->arg_info, dst->num_args, deallocate);
3586 + if(dst->try_catch_array) deallocate(dst->try_catch_array);
3587 + if(dst->doc_comment) deallocate(dst->doc_comment);
3588 +#else
3589 + if(dst->arg_types) deallocate(dst->arg_types);
3590 +#endif
3591 + if(dst->opcodes) deallocate(dst->opcodes);
3592 + if(dst->brk_cont_array) deallocate(dst->brk_cont_array);
3593 + if(dst->static_variables) my_free_hashtable(dst->static_variables, (ht_free_fun_t)my_free_zval_ptr, (apc_free_t)deallocate);
3594 +#ifdef ZEND_ENGINE_2_1
3595 + if (dst->vars) {
3596 + for(i=0; i < dst->last_var; i++) {
3597 + if(dst->vars[i].name) deallocate(dst->vars[i].name);
3598 + }
3599 + deallocate(dst->vars);
3600 + }
3601 +#endif
3602 + if(local_dst_alloc) deallocate(dst);
3603 + return NULL;
3604 +}
3605 +/* }}} */
3606 +
3607 +/* {{{ apc_copy_new_functions */
3608 +apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
3609 +{
3610 + apc_function_t* array;
3611 + int new_count; /* number of new functions in table */
3612 + int i;
3613 +
3614 + new_count = zend_hash_num_elements(CG(function_table)) - old_count;
3615 + assert(new_count >= 0);
3616 +
3617 + CHECK(array =
3618 + (apc_function_t*)
3619 + allocate(sizeof(apc_function_t) * (new_count+1)));
3620 +
3621 + if (new_count == 0) {
3622 + array[0].function = NULL;
3623 + return array;
3624 + }
3625 +
3626 + /* Skip the first `old_count` functions in the table */
3627 + zend_hash_internal_pointer_reset(CG(function_table));
3628 + for (i = 0; i < old_count; i++) {
3629 + zend_hash_move_forward(CG(function_table));
3630 + }
3631 +
3632 + /* Add the next `new_count` functions to our array */
3633 + for (i = 0; i < new_count; i++) {
3634 + char* key;
3635 + uint key_size;
3636 + zend_function* fun;
3637 +
3638 + zend_hash_get_current_key_ex(CG(function_table),
3639 + &key,
3640 + &key_size,
3641 + NULL,
3642 + 0,
3643 + NULL);
3644 +
3645 + zend_hash_get_current_data(CG(function_table), (void**) &fun);
3646 +
3647 + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
3648 + int ii;
3649 + for(ii=i-1; ii>=0; ii--) {
3650 + deallocate(array[ii].name);
3651 + my_free_function(array[ii].function, deallocate);
3652 + }
3653 + deallocate(array);
3654 + return NULL;
3655 + }
3656 + array[i].name_len = (int) key_size-1;
3657 + if(!(array[i].function = my_copy_function(NULL, fun, allocate, deallocate))) {
3658 + int ii;
3659 + deallocate(array[i].name);
3660 + for(ii=i-1; ii>=0; ii--) {
3661 + deallocate(array[ii].name);
3662 + my_free_function(array[ii].function, deallocate);
3663 + }
3664 + deallocate(array);
3665 + return NULL;
3666 + }
3667 + zend_hash_move_forward(CG(function_table));
3668 + }
3669 +
3670 + array[i].function = NULL;
3671 + return array;
3672 +}
3673 +/* }}} */
3674 +
3675 +/* {{{ apc_copy_new_classes */
3676 +apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
3677 +{
3678 + apc_class_t* array;
3679 + int new_count; /* number of new classes in table */
3680 + int i;
3681 +
3682 + new_count = zend_hash_num_elements(CG(class_table)) - old_count;
3683 + assert(new_count >= 0);
3684 +
3685 + CHECK(array =
3686 + (apc_class_t*)
3687 + allocate(sizeof(apc_class_t)*(new_count+1)));
3688 +
3689 + if (new_count == 0) {
3690 + array[0].class_entry = NULL;
3691 + return array;
3692 + }
3693 +
3694 + /* Skip the first `old_count` classes in the table */
3695 + zend_hash_internal_pointer_reset(CG(class_table));
3696 + for (i = 0; i < old_count; i++) {
3697 + zend_hash_move_forward(CG(class_table));
3698 + }
3699 +
3700 + /* Add the next `new_count` classes to our array */
3701 + for (i = 0; i < new_count; i++) {
3702 + char* key;
3703 + uint key_size;
3704 + zend_class_entry* elem = NULL;
3705 +
3706 + array[i].class_entry = NULL;
3707 +
3708 + zend_hash_get_current_key_ex(CG(class_table),
3709 + &key,
3710 + &key_size,
3711 + NULL,
3712 + 0,
3713 + NULL);
3714 +
3715 + zend_hash_get_current_data(CG(class_table), (void**) &elem);
3716 +
3717 +
3718 +#ifdef ZEND_ENGINE_2
3719 + elem = *((zend_class_entry**)elem);
3720 +#endif
3721 +
3722 + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
3723 + int ii;
3724 +
3725 + for(ii=i-1; ii>=0; ii--) {
3726 + deallocate(array[ii].name);
3727 + my_destroy_class_entry(array[ii].class_entry, deallocate);
3728 + deallocate(array[ii].class_entry);
3729 + }
3730 + deallocate(array);
3731 + return NULL;
3732 + }
3733 + array[i].name_len = (int) key_size-1;
3734 + if(!(array[i].class_entry = my_copy_class_entry(NULL, elem, allocate, deallocate))) {
3735 + int ii;
3736 +
3737 + deallocate(array[i].name);
3738 + for(ii=i-1; ii>=0; ii--) {
3739 + deallocate(array[ii].name);
3740 + my_destroy_class_entry(array[ii].class_entry, deallocate);
3741 + deallocate(array[ii].class_entry);
3742 + }
3743 + deallocate(array);
3744 + return NULL;
3745 + }
3746 +
3747 + /*
3748 + * If the class has a pointer to its parent class, save the parent
3749 + * name so that we can enable compile-time inheritance when we reload
3750 + * the child class; otherwise, set the parent name to null and scan
3751 + * the op_array to determine if this class inherits from some base
3752 + * class at execution-time.
3753 + */
3754 +
3755 + if (elem->parent) {
3756 + if(!(array[i].parent_name =
3757 + apc_xstrdup(elem->parent->name, allocate))) {
3758 + int ii;
3759 +
3760 + for(ii=i; ii>=0; ii--) {
3761 + deallocate(array[ii].name);
3762 + my_destroy_class_entry(array[ii].class_entry, deallocate);
3763 + deallocate(array[ii].class_entry);
3764 + if(ii==i) continue;
3765 + if(array[ii].parent_name) deallocate(array[ii].parent_name);
3766 + }
3767 + deallocate(array);
3768 + return NULL;
3769 + }
3770 + array[i].is_derived = 1;
3771 + }
3772 + else {
3773 + array[i].parent_name = NULL;
3774 + array[i].is_derived = is_derived_class(op_array, key, key_size);
3775 + }
3776 +
3777 + zend_hash_move_forward(CG(class_table));
3778 + }
3779 +
3780 + array[i].class_entry = NULL;
3781 + return array;
3782 +}
3783 +/* }}} */
3784 +
3785 +/* {{{ my_destroy_zval_ptr */
3786 +static void my_destroy_zval_ptr(zval** src, apc_free_t deallocate)
3787 +{
3788 + assert(src != NULL);
3789 + if(my_destroy_zval(src[0], deallocate) == SUCCESS) {
3790 + deallocate(src[0]);
3791 + }
3792 +}
3793 +/* }}} */
3794 +
3795 +/* {{{ my_destroy_zval */
3796 +static int my_destroy_zval(zval* src, apc_free_t deallocate)
3797 +{
3798 + zval **tmp;
3799 + TSRMLS_FETCH();
3800 +
3801 + switch (src->type & ~IS_CONSTANT_INDEX) {
3802 + case IS_RESOURCE:
3803 + case IS_BOOL:
3804 + case IS_LONG:
3805 + case IS_DOUBLE:
3806 + case IS_NULL:
3807 + break;
3808 +
3809 + case IS_CONSTANT:
3810 + case IS_STRING:
3811 +#ifndef ZEND_ENGINE_2
3812 + case FLAG_IS_BC:
3813 +#endif
3814 + deallocate(src->value.str.val);
3815 + break;
3816 +
3817 + case IS_ARRAY:
3818 +
3819 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
3820 + if(APCG(copied_zvals)) {
3821 + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
3822 + (*tmp)->refcount--;
3823 + return FAILURE;
3824 + }
3825 + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&src, sizeof(zval*), NULL);
3826 + }
3827 + /* fall through */
3828 +
3829 + case IS_CONSTANT_ARRAY:
3830 + my_free_hashtable(src->value.ht,
3831 + (ht_free_fun_t) my_free_zval_ptr,
3832 + deallocate);
3833 + break;
3834 +
3835 + case IS_OBJECT:
3836 +#ifndef ZEND_ENGINE_2
3837 + my_destroy_class_entry(src->value.obj.ce, deallocate);
3838 + deallocate(src->value.obj.ce);
3839 + my_free_hashtable(src->value.obj.properties,
3840 + (ht_free_fun_t) my_free_zval_ptr,
3841 + deallocate);
3842 +#endif
3843 + break;
3844 +