c3537588ff0bf2b462fcd98d5e94013cdb461d96
[openwrt/svn-archive/archive.git] / lang / php5 / patches / 005-APC.patch
1 diff -ruN php-5.1.6.old/ext/apc/apc.c php-5.1.6/ext/APC/apc.c
2 --- php-5.1.6.old/ext/apc/apc.c 1969-12-31 18:00:00.000000000 -0600
3 +++ php-5.1.6/ext/apc/apc.c 2007-04-02 18:05:30.000000000 -0500
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.17 2007/03/17 14:01:41 gopalv 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_DBG
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 -ruN php-5.1.6.old/ext/apc/apc_cache.c php-5.1.6/ext/APC/apc_cache.c
560 --- php-5.1.6.old/ext/apc/apc_cache.c 1969-12-31 18:00:00.000000000 -0600
561 +++ php-5.1.6/ext/apc/apc_cache.c 2007-04-02 18:05:30.000000000 -0500
562 @@ -0,0 +1,1328 @@
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.140 2007/04/02 22:57:10 rasmus 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 + apc_cache_entry_t *value; /* shallow copy of slot->value */
663 + local_slot_t *next; /* only for dead list */
664 +};
665 +/* }}} */
666 +/* {{{ struct definition apc_local_cache_t */
667 +struct apc_local_cache_t {
668 + apc_cache_t* shmcache; /* the real cache in shm */
669 + local_slot_t* slots; /* process (local) cache of objects */
670 + local_slot_t* dead_list; /* list of objects pending removal */
671 + int num_slots; /* number of slots in cache */
672 + int ttl; /* time to live */
673 + int num_hits; /* number of hits */
674 + int generation; /* every generation lives between expunges */
675 +};
676 +/* }}} */
677 +
678 +/* {{{ key_equals */
679 +#define key_equals(a, b) (a.inode==b.inode && a.device==b.device)
680 +/* }}} */
681 +
682 +/* {{{ hash */
683 +static unsigned int hash(apc_cache_key_t key)
684 +{
685 + return key.data.file.device + key.data.file.inode;
686 +}
687 +/* }}} */
688 +
689 +/* {{{ string_nhash_8 */
690 +static unsigned int string_nhash_8(const char *s, size_t len)
691 +{
692 + register const unsigned int *iv = (const unsigned int *)s;
693 + register unsigned int h = 0;
694 + register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int)));
695 +
696 + for(;iv<e;iv++) {
697 + h += *iv;
698 + h = (h << 7) | (h >> ((8*sizeof(unsigned int)) - 7));
699 + }
700 + s = (const char *)iv;
701 + for(len %= sizeof(unsigned int);len;len--) {
702 + h += *(s++);
703 + }
704 + h ^= (h >> 13);
705 + h ^= (h >> 7);
706 + return h;
707 +}
708 +/* }}} */
709 +
710 +/* {{{ make_slot */
711 +slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t)
712 +{
713 + slot_t* p = apc_sma_malloc(sizeof(slot_t));
714 + if (!p) return NULL;
715 +
716 + if(value->type == APC_CACHE_ENTRY_USER) {
717 + char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc);
718 + if (!identifier) {
719 + apc_sma_free(p);
720 + return NULL;
721 + }
722 + key.data.user.identifier = identifier;
723 + } else if(key.type == APC_CACHE_KEY_FPFILE) {
724 + char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc);
725 + if (!fullpath) {
726 + apc_sma_free(p);
727 + return NULL;
728 + }
729 + key.data.fpfile.fullpath = fullpath;
730 + }
731 + p->key = key;
732 + p->value = value;
733 + p->next = next;
734 + p->num_hits = 0;
735 + p->creation_time = t;
736 + p->access_time = t;
737 + p->deletion_time = 0;
738 + return p;
739 +}
740 +/* }}} */
741 +
742 +/* {{{ free_slot */
743 +static void free_slot(slot_t* slot)
744 +{
745 + if(slot->value->type == APC_CACHE_ENTRY_USER) {
746 + apc_sma_free((char *)slot->key.data.user.identifier);
747 + } else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
748 + apc_sma_free((char *)slot->key.data.fpfile.fullpath);
749 + }
750 + apc_cache_free_entry(slot->value);
751 + apc_sma_free(slot);
752 +}
753 +/* }}} */
754 +
755 +/* {{{ remove_slot */
756 +static void remove_slot(apc_cache_t* cache, slot_t** slot)
757 +{
758 + slot_t* dead = *slot;
759 + *slot = (*slot)->next;
760 +
761 + cache->header->mem_size -= dead->value->mem_size;
762 + cache->header->num_entries--;
763 + if (dead->value->ref_count <= 0) {
764 + free_slot(dead);
765 + }
766 + else {
767 + dead->next = cache->header->deleted_list;
768 + dead->deletion_time = time(0);
769 + cache->header->deleted_list = dead;
770 + }
771 +}
772 +/* }}} */
773 +
774 +/* {{{ process_pending_removals */
775 +static void process_pending_removals(apc_cache_t* cache)
776 +{
777 + slot_t** slot;
778 + time_t now;
779 +
780 + /* This function scans the list of removed cache entries and deletes any
781 + * entry whose reference count is zero (indicating that it is no longer
782 + * being executed) or that has been on the pending list for more than
783 + * cache->gc_ttl seconds (we issue a warning in the latter case).
784 + */
785 +
786 + if (!cache->header->deleted_list)
787 + return;
788 +
789 + slot = &cache->header->deleted_list;
790 + now = time(0);
791 +
792 + while (*slot != NULL) {
793 + int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0;
794 +
795 + if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) {
796 + slot_t* dead = *slot;
797 +
798 + if (dead->value->ref_count > 0) {
799 + switch(dead->value->type) {
800 + case APC_CACHE_ENTRY_FILE:
801 + apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) "
802 + "was on gc-list for %d seconds", dead->value->data.file.filename,
803 + dead->key.data.file.device, dead->key.data.file.inode, gc_sec);
804 + break;
805 + case APC_CACHE_ENTRY_USER:
806 + apc_log(APC_WARNING, "GC cache entry '%s' "
807 + "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec);
808 + break;
809 + }
810 + }
811 + *slot = dead->next;
812 + free_slot(dead);
813 + }
814 + else {
815 + slot = &(*slot)->next;
816 + }
817 + }
818 +}
819 +/* }}} */
820 +
821 +/* {{{ prevent_garbage_collection */
822 +static void prevent_garbage_collection(apc_cache_entry_t* entry)
823 +{
824 + /* set reference counts on zend objects to an arbitrarily high value to
825 + * prevent garbage collection after execution */
826 +
827 + enum { BIG_VALUE = 1000 };
828 +
829 + if(entry->data.file.op_array) {
830 + entry->data.file.op_array->refcount[0] = BIG_VALUE;
831 + }
832 + if (entry->data.file.functions) {
833 + int i;
834 + apc_function_t* fns = entry->data.file.functions;
835 + for (i=0; fns[i].function != NULL; i++) {
836 +#ifdef ZEND_ENGINE_2
837 + *(fns[i].function->op_array.refcount) = BIG_VALUE;
838 +#else
839 + fns[i].function->op_array.refcount[0] = BIG_VALUE;
840 +#endif
841 + }
842 + }
843 + if (entry->data.file.classes) {
844 + int i;
845 + apc_class_t* classes = entry->data.file.classes;
846 + for (i=0; classes[i].class_entry != NULL; i++) {
847 +#ifdef ZEND_ENGINE_2
848 + classes[i].class_entry->refcount = BIG_VALUE;
849 +#else
850 + classes[i].class_entry->refcount[0] = BIG_VALUE;
851 +#endif
852 + }
853 + }
854 +}
855 +/* }}} */
856 +
857 +/* {{{ apc_cache_create */
858 +apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl)
859 +{
860 + apc_cache_t* cache;
861 + int cache_size;
862 + int num_slots;
863 + int i;
864 +
865 + num_slots = size_hint > 0 ? size_hint*2 : 2000;
866 +
867 + cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t));
868 + cache_size = sizeof(header_t) + num_slots*sizeof(slot_t*);
869 +
870 + cache->shmaddr = apc_sma_malloc(cache_size);
871 + memset(cache->shmaddr, 0, cache_size);
872 +
873 + cache->header = (header_t*) cache->shmaddr;
874 + cache->header->num_hits = 0;
875 + cache->header->num_misses = 0;
876 + cache->header->deleted_list = NULL;
877 + cache->header->start_time = time(NULL);
878 + cache->header->expunges = 0;
879 + cache->header->busy = 0;
880 +
881 + cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(header_t));
882 + cache->num_slots = num_slots;
883 + cache->gc_ttl = gc_ttl;
884 + cache->ttl = ttl;
885 + CREATE_LOCK(cache->header->lock);
886 +#if NONBLOCKING_LOCK_AVAILABLE
887 + CREATE_LOCK(cache->header->wrlock);
888 +#endif
889 + for (i = 0; i < num_slots; i++) {
890 + cache->slots[i] = NULL;
891 + }
892 +
893 + return cache;
894 +}
895 +/* }}} */
896 +
897 +/* {{{ apc_cache_destroy */
898 +void apc_cache_destroy(apc_cache_t* cache)
899 +{
900 + DESTROY_LOCK(cache);
901 + apc_efree(cache);
902 +}
903 +/* }}} */
904 +
905 +/* {{{ apc_cache_clear */
906 +void apc_cache_clear(apc_cache_t* cache)
907 +{
908 + int i;
909 +
910 + if(!cache) return;
911 +
912 + LOCK(cache);
913 + cache->header->busy = 1;
914 + cache->header->num_hits = 0;
915 + cache->header->num_misses = 0;
916 + cache->header->start_time = time(NULL);
917 + cache->header->expunges = 0;
918 +
919 + for (i = 0; i < cache->num_slots; i++) {
920 + slot_t* p = cache->slots[i];
921 + while (p) {
922 + remove_slot(cache, &p);
923 + }
924 + cache->slots[i] = NULL;
925 + }
926 +
927 + cache->header->busy = 0;
928 + UNLOCK(cache);
929 +}
930 +/* }}} */
931 +
932 +/* {{{ apc_cache_expunge */
933 +void apc_cache_expunge(apc_cache_t* cache, time_t t)
934 +{
935 + int i;
936 +
937 + if(!cache) return;
938 +
939 + if(!cache->ttl) {
940 + /*
941 + * If cache->ttl is not set, we wipe out the entire cache when
942 + * we run out of space.
943 + */
944 + LOCK(cache);
945 + cache->header->busy = 1;
946 + cache->header->expunges++;
947 + for (i = 0; i < cache->num_slots; i++) {
948 + slot_t* p = cache->slots[i];
949 + while (p) {
950 + remove_slot(cache, &p);
951 + }
952 + cache->slots[i] = NULL;
953 + }
954 + cache->header->busy = 0;
955 + UNLOCK(cache);
956 + } else {
957 + slot_t **p;
958 +
959 + /*
960 + * If the ttl for the cache is set we walk through and delete stale
961 + * entries. For the user cache that is slightly confusing since
962 + * we have the individual entry ttl's we can look at, but that would be
963 + * too much work. So if you want the user cache expunged, set a high
964 + * default apc.user_ttl and still provide a specific ttl for each entry
965 + * on insert
966 + */
967 +
968 + LOCK(cache);
969 + cache->header->busy = 1;
970 + cache->header->expunges++;
971 + for (i = 0; i < cache->num_slots; i++) {
972 + p = &cache->slots[i];
973 + while(*p) {
974 + /*
975 + * For the user cache we look at the individual entry ttl values
976 + * and if not set fall back to the default ttl for the user cache
977 + */
978 + if((*p)->value->type == APC_CACHE_ENTRY_USER) {
979 + if((*p)->value->data.user.ttl) {
980 + if((*p)->creation_time + (*p)->value->data.user.ttl < t) {
981 + remove_slot(cache, p);
982 + continue;
983 + }
984 + } else if(cache->ttl) {
985 + if((*p)->creation_time + cache->ttl < t) {
986 + remove_slot(cache, p);
987 + continue;
988 + }
989 + }
990 + } else if((*p)->access_time < (t - cache->ttl)) {
991 + remove_slot(cache, p);
992 + continue;
993 + }
994 + p = &(*p)->next;
995 + }
996 + }
997 + cache->header->busy = 0;
998 + UNLOCK(cache);
999 + }
1000 +}
1001 +/* }}} */
1002 +
1003 +/* {{{ apc_cache_insert */
1004 +int apc_cache_insert(apc_cache_t* cache,
1005 + apc_cache_key_t key,
1006 + apc_cache_entry_t* value,
1007 + time_t t)
1008 +{
1009 + slot_t** slot;
1010 +
1011 + if (!value) {
1012 + return 0;
1013 + }
1014 +
1015 +#ifdef __DEBUG_APC__
1016 + fprintf(stderr,"Inserting [%s]\n", value->data.file.filename);
1017 +#endif
1018 +
1019 + LOCK(cache);
1020 + process_pending_removals(cache);
1021 +
1022 + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
1023 + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1024 +
1025 + while(*slot) {
1026 + if(key.type == (*slot)->key.type) {
1027 + if(key.type == APC_CACHE_KEY_FILE) {
1028 + if(key_equals((*slot)->key.data.file, key.data.file)) {
1029 + /* If existing slot for the same device+inode is different, remove it and insert the new version */
1030 + if ((*slot)->key.mtime != key.mtime) {
1031 + remove_slot(cache, slot);
1032 + break;
1033 + }
1034 + UNLOCK(cache);
1035 + return 0;
1036 + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
1037 + remove_slot(cache, slot);
1038 + continue;
1039 + }
1040 + } else { /* APC_CACHE_KEY_FPFILE */
1041 + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1042 + /* Hrm.. it's already here, remove it and insert new one */
1043 + remove_slot(cache, slot);
1044 + break;
1045 + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
1046 + remove_slot(cache, slot);
1047 + continue;
1048 + }
1049 + }
1050 + }
1051 + slot = &(*slot)->next;
1052 + }
1053 +
1054 + if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
1055 + UNLOCK(cache);
1056 + return -1;
1057 + }
1058 +
1059 + cache->header->mem_size += value->mem_size;
1060 + cache->header->num_entries++;
1061 + cache->header->num_inserts++;
1062 +
1063 + UNLOCK(cache);
1064 + return 1;
1065 +}
1066 +/* }}} */
1067 +
1068 +/* {{{ apc_cache_user_insert */
1069 +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)
1070 +{
1071 + slot_t** slot;
1072 + size_t* mem_size_ptr = NULL;
1073 +
1074 + if (!value) {
1075 + return 0;
1076 + }
1077 +
1078 + LOCK(cache);
1079 + process_pending_removals(cache);
1080 +
1081 + slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots];
1082 +
1083 + if (APCG(mem_size_ptr) != NULL) {
1084 + mem_size_ptr = APCG(mem_size_ptr);
1085 + APCG(mem_size_ptr) = NULL;
1086 + }
1087 +
1088 + while (*slot) {
1089 + if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) {
1090 + /*
1091 + * At this point we have found the user cache entry. If we are doing
1092 + * an exclusive insert (apc_add) we are going to bail right away if
1093 + * the user entry already exists and it has no ttl, or
1094 + * there is a ttl and the entry has not timed out yet.
1095 + */
1096 + if(exclusive && ( !(*slot)->value->data.user.ttl ||
1097 + ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t )
1098 + ) ) {
1099 + UNLOCK(cache);
1100 + return 0;
1101 + }
1102 + remove_slot(cache, slot);
1103 + break;
1104 + } else
1105 + /*
1106 + * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of
1107 + * slot entries so we don't always have to skip past a bunch of stale entries. We check
1108 + * for staleness here and get rid of them by first checking to see if the cache has a global
1109 + * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly
1110 + * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl
1111 + */
1112 + if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) ||
1113 + ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) {
1114 + remove_slot(cache, slot);
1115 + continue;
1116 + }
1117 + slot = &(*slot)->next;
1118 + }
1119 +
1120 + if (mem_size_ptr != NULL) {
1121 + APCG(mem_size_ptr) = mem_size_ptr;
1122 + }
1123 +
1124 + if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
1125 + UNLOCK(cache);
1126 + return 0;
1127 + }
1128 + if (APCG(mem_size_ptr) != NULL) {
1129 + value->mem_size = *APCG(mem_size_ptr);
1130 + cache->header->mem_size += *APCG(mem_size_ptr);
1131 + }
1132 + cache->header->num_entries++;
1133 + cache->header->num_inserts++;
1134 +
1135 + UNLOCK(cache);
1136 + return 1;
1137 +}
1138 +/* }}} */
1139 +
1140 +/* {{{ apc_cache_find_slot */
1141 +slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t)
1142 +{
1143 + slot_t** slot;
1144 + volatile slot_t* retval = NULL;
1145 +
1146 + LOCK(cache);
1147 + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
1148 + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1149 +
1150 + while (*slot) {
1151 + if(key.type == (*slot)->key.type) {
1152 + if(key.type == APC_CACHE_KEY_FILE) {
1153 + if(key_equals((*slot)->key.data.file, key.data.file)) {
1154 + if((*slot)->key.mtime != key.mtime) {
1155 + remove_slot(cache, slot);
1156 + cache->header->num_misses++;
1157 + UNLOCK(cache);
1158 + return NULL;
1159 + }
1160 + (*slot)->num_hits++;
1161 + (*slot)->value->ref_count++;
1162 + (*slot)->access_time = t;
1163 + prevent_garbage_collection((*slot)->value);
1164 + cache->header->num_hits++;
1165 + retval = *slot;
1166 + UNLOCK(cache);
1167 + return (slot_t*)retval;
1168 + }
1169 + } else { /* APC_CACHE_KEY_FPFILE */
1170 + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1171 + /* TTL Check ? */
1172 + (*slot)->num_hits++;
1173 + (*slot)->value->ref_count++;
1174 + (*slot)->access_time = t;
1175 + prevent_garbage_collection((*slot)->value);
1176 + cache->header->num_hits++;
1177 + retval = *slot;
1178 + UNLOCK(cache);
1179 + return (slot_t*)retval;
1180 + }
1181 + }
1182 + }
1183 + slot = &(*slot)->next;
1184 + }
1185 + cache->header->num_misses++;
1186 + UNLOCK(cache);
1187 + return NULL;
1188 +}
1189 +/* }}} */
1190 +
1191 +/* {{{ apc_cache_find */
1192 +apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t)
1193 +{
1194 + slot_t * slot = apc_cache_find_slot(cache, key, t);
1195 + return (slot) ? slot->value : NULL;
1196 +}
1197 +/* }}} */
1198 +
1199 +/* {{{ apc_cache_user_find */
1200 +apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t)
1201 +{
1202 + slot_t** slot;
1203 + volatile apc_cache_entry_t* value = NULL;
1204 +
1205 + LOCK(cache);
1206 +
1207 + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
1208 +
1209 + while (*slot) {
1210 + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
1211 + /* Check to make sure this entry isn't expired by a hard TTL */
1212 + if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) {
1213 + remove_slot(cache, slot);
1214 + UNLOCK(cache);
1215 + return NULL;
1216 + }
1217 + /* Otherwise we are fine, increase counters and return the cache entry */
1218 + (*slot)->num_hits++;
1219 + (*slot)->value->ref_count++;
1220 + (*slot)->access_time = t;
1221 +
1222 + cache->header->num_hits++;
1223 + value = (*slot)->value;
1224 + UNLOCK(cache);
1225 + return (apc_cache_entry_t*)value;
1226 + }
1227 + slot = &(*slot)->next;
1228 + }
1229 +
1230 + UNLOCK(cache);
1231 + return NULL;
1232 +}
1233 +/* }}} */
1234 +
1235 +/* {{{ apc_cache_user_delete */
1236 +int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
1237 +{
1238 + slot_t** slot;
1239 +
1240 + LOCK(cache);
1241 +
1242 + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
1243 +
1244 + while (*slot) {
1245 + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
1246 + remove_slot(cache, slot);
1247 + UNLOCK(cache);
1248 + return 1;
1249 + }
1250 + slot = &(*slot)->next;
1251 + }
1252 +
1253 + UNLOCK(cache);
1254 + return 0;
1255 +}
1256 +/* }}} */
1257 +
1258 +/* {{{ apc_cache_release */
1259 +void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry)
1260 +{
1261 + /* local cache refcount-- is done in apc_local_cache_cleanup */
1262 + if(entry->local) return;
1263 +
1264 + LOCK(cache);
1265 + entry->ref_count--;
1266 + UNLOCK(cache);
1267 +}
1268 +/* }}} */
1269 +
1270 +/* {{{ apc_cache_make_file_key */
1271 +int apc_cache_make_file_key(apc_cache_key_t* key,
1272 + const char* filename,
1273 + const char* include_path,
1274 + time_t t
1275 + TSRMLS_DC)
1276 +{
1277 + static char canon_path[MAXPATHLEN];
1278 + struct stat *tmp_buf=NULL;
1279 + struct apc_fileinfo_t fileinfo = { {0}, };
1280 + int len;
1281 +
1282 + assert(key != NULL);
1283 +
1284 + if (!filename || !SG(request_info).path_translated) {
1285 +#ifdef __DEBUG_APC__
1286 + fprintf(stderr,"No filename and no path_translated - bailing\n");
1287 +#endif
1288 + return 0;
1289 + }
1290 +
1291 + len = strlen(filename);
1292 + if(APCG(fpstat)==0) {
1293 + if(IS_ABSOLUTE_PATH(filename,len)) {
1294 + key->data.fpfile.fullpath = filename;
1295 + key->data.fpfile.fullpath_len = len;
1296 + key->mtime = t;
1297 + key->type = APC_CACHE_KEY_FPFILE;
1298 + } else {
1299 + if(!realpath(filename, canon_path)) {
1300 + fprintf(stderr, "realpath failed to canonicalize %s - bailing\n", filename);
1301 + return 0;
1302 + }
1303 + key->data.fpfile.fullpath = canon_path;
1304 + key->data.fpfile.fullpath_len = strlen(canon_path);
1305 + key->mtime = t;
1306 + key->type = APC_CACHE_KEY_FPFILE;
1307 + }
1308 + return 1;
1309 + }
1310 +
1311 + if(!strcmp(SG(request_info).path_translated, filename)) {
1312 + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */
1313 + }
1314 + if(tmp_buf) {
1315 + fileinfo.st_buf = *tmp_buf;
1316 + } else {
1317 + if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
1318 +#ifdef __DEBUG_APC__
1319 + fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated);
1320 +#endif
1321 + return 0;
1322 + }
1323 + }
1324 +
1325 + if(APCG(max_file_size) < fileinfo.st_buf.st_size) {
1326 +#ifdef __DEBUG_APC__
1327 + fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.st_size);
1328 +#endif
1329 + return 0;
1330 + }
1331 +
1332 + /*
1333 + * This is a bit of a hack.
1334 + *
1335 + * Here I am checking to see if the file is at least 2 seconds old.
1336 + * The idea is that if the file is currently being written to then its
1337 + * mtime is going to match or at most be 1 second off of the current
1338 + * request time and we want to avoid caching files that have not been
1339 + * completely written. Of course, people should be using atomic
1340 + * mechanisms to push files onto live web servers, but adding this
1341 + * tiny safety is easier than educating the world. This is now
1342 + * configurable, but the default is still 2 seconds.
1343 + */
1344 + if(APCG(file_update_protection) && (t - fileinfo.st_buf.st_mtime < APCG(file_update_protection))) {
1345 +#ifdef __DEBUG_APC__
1346 + fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.st_mtime);
1347 +#endif
1348 + return 0;
1349 + }
1350 +
1351 + key->data.file.device = fileinfo.st_buf.st_dev;
1352 + key->data.file.inode = fileinfo.st_buf.st_ino;
1353 + /*
1354 + * If working with content management systems that like to munge the mtime,
1355 + * it might be appropriate to key off of the ctime to be immune to systems
1356 + * that try to backdate a template. If the mtime is set to something older
1357 + * than the previous mtime of a template we will obviously never see this
1358 + * "older" template. At some point the Smarty templating system did this.
1359 + * I generally disagree with using the ctime here because you lose the
1360 + * ability to warm up new content by saving it to a temporary file, hitting
1361 + * it once to cache it and then renaming it into its permanent location so
1362 + * set the apc.stat_ctime=true to enable this check.
1363 + */
1364 + if(APCG(stat_ctime)) {
1365 + key->mtime = (fileinfo.st_buf.st_ctime > fileinfo.st_buf.st_mtime) ? fileinfo.st_buf.st_ctime : fileinfo.st_buf.st_mtime;
1366 + } else {
1367 + key->mtime = fileinfo.st_buf.st_mtime;
1368 + }
1369 + key->type = APC_CACHE_KEY_FILE;
1370 + return 1;
1371 +}
1372 +/* }}} */
1373 +
1374 +/* {{{ apc_cache_make_user_key */
1375 +int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t)
1376 +{
1377 + assert(key != NULL);
1378 +
1379 + if (!identifier)
1380 + return 0;
1381 +
1382 + key->data.user.identifier = identifier;
1383 + key->data.user.identifier_len = identifier_len;
1384 + key->mtime = t;
1385 + key->type = APC_CACHE_KEY_USER;
1386 + return 1;
1387 +}
1388 +/* }}} */
1389 +
1390 +/* {{{ apc_cache_make_file_entry */
1391 +apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
1392 + zend_op_array* op_array,
1393 + apc_function_t* functions,
1394 + apc_class_t* classes)
1395 +{
1396 + apc_cache_entry_t* entry;
1397 +
1398 + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
1399 + if (!entry) return NULL;
1400 +
1401 + entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc);
1402 + if(!entry->data.file.filename) {
1403 +#ifdef __DEBUG_APC__
1404 + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n");
1405 +#endif
1406 + apc_sma_free(entry);
1407 + return NULL;
1408 + }
1409 +#ifdef __DEBUG_APC__
1410 + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename);
1411 +#endif
1412 + entry->data.file.op_array = op_array;
1413 + entry->data.file.functions = functions;
1414 + entry->data.file.classes = classes;
1415 + entry->type = APC_CACHE_ENTRY_FILE;
1416 + entry->ref_count = 0;
1417 + entry->mem_size = 0;
1418 + entry->autofiltered = 0;
1419 + entry->local = 0;
1420 + return entry;
1421 +}
1422 +/* }}} */
1423 +
1424 +/* {{{ apc_cache_store_zval */
1425 +zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
1426 +{
1427 + smart_str buf = {0};
1428 + php_serialize_data_t var_hash;
1429 + TSRMLS_FETCH();
1430 +
1431 + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1432 + if(!dst) {
1433 + CHECK(dst = (zval*) allocate(sizeof(zval)));
1434 + }
1435 +
1436 + PHP_VAR_SERIALIZE_INIT(var_hash);
1437 + php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC);
1438 + PHP_VAR_SERIALIZE_DESTROY(var_hash);
1439 +
1440 + dst->type = IS_NULL; /* in case we fail */
1441 + if(buf.c) {
1442 + dst->type = src->type & ~IS_CONSTANT_INDEX;
1443 + dst->value.str.len = buf.len;
1444 + CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate));
1445 + dst->type = src->type;
1446 + smart_str_free(&buf);
1447 + }
1448 + return dst;
1449 + } else {
1450 +
1451 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1452 + HashTable *old = APCG(copied_zvals);
1453 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1454 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1455 +
1456 + dst = apc_copy_zval(dst, src, allocate, deallocate);
1457 +
1458 + if(APCG(copied_zvals)) {
1459 + zend_hash_destroy(APCG(copied_zvals));
1460 + efree(APCG(copied_zvals));
1461 + }
1462 +
1463 + APCG(copied_zvals) = old;
1464 +
1465 + return dst;
1466 + }
1467 +}
1468 +/* }}} */
1469 +
1470 +/* {{{ apc_cache_fetch_zval */
1471 +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
1472 +{
1473 + TSRMLS_FETCH();
1474 + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1475 + php_unserialize_data_t var_hash;
1476 + const unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
1477 +
1478 + PHP_VAR_UNSERIALIZE_INIT(var_hash);
1479 + if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) {
1480 + PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1481 + zval_dtor(dst);
1482 + 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));
1483 + dst->type = IS_NULL;
1484 + }
1485 + PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1486 + return dst;
1487 + } else {
1488 +
1489 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1490 + HashTable *old = APCG(copied_zvals);
1491 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1492 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1493 +
1494 + dst = apc_copy_zval(dst, src, allocate, deallocate);
1495 +
1496 + if(APCG(copied_zvals)) {
1497 + zend_hash_destroy(APCG(copied_zvals));
1498 + efree(APCG(copied_zvals));
1499 + }
1500 +
1501 + APCG(copied_zvals) = old;
1502 +
1503 + return dst;
1504 + }
1505 +}
1506 +/* }}} */
1507 +
1508 +/* {{{ apc_cache_free_zval */
1509 +void apc_cache_free_zval(zval* src, apc_free_t deallocate)
1510 +{
1511 + TSRMLS_FETCH();
1512 + if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
1513 + if (src->value.str.val) {
1514 + deallocate(src->value.str.val);
1515 + }
1516 + deallocate(src);
1517 + } else {
1518 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
1519 + HashTable *old = APCG(copied_zvals);
1520 + APCG(copied_zvals) = emalloc(sizeof(HashTable));
1521 + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
1522 +
1523 + apc_free_zval(src, deallocate);
1524 +
1525 + if(APCG(copied_zvals)) {
1526 + zend_hash_destroy(APCG(copied_zvals));
1527 + efree(APCG(copied_zvals));
1528 + }
1529 +
1530 + APCG(copied_zvals) = old;
1531 + }
1532 +}
1533 +/* }}} */
1534 +
1535 +/* {{{ apc_cache_make_user_entry */
1536 +apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl)
1537 +{
1538 + apc_cache_entry_t* entry;
1539 +
1540 + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
1541 + if (!entry) return NULL;
1542 +
1543 + entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc);
1544 + entry->data.user.info_len = info_len;
1545 + if(!entry->data.user.info) {
1546 + apc_sma_free(entry);
1547 + return NULL;
1548 + }
1549 + entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free);
1550 + if(!entry->data.user.val) {
1551 + apc_sma_free(entry->data.user.info);
1552 + apc_sma_free(entry);
1553 + return NULL;
1554 + }
1555 + INIT_PZVAL(entry->data.user.val);
1556 + entry->data.user.ttl = ttl;
1557 + entry->type = APC_CACHE_ENTRY_USER;
1558 + entry->ref_count = 0;
1559 + entry->mem_size = 0;
1560 + entry->autofiltered = 0;
1561 + entry->local = 0;
1562 + return entry;
1563 +}
1564 +/* }}} */
1565 +
1566 +/* {{{ apc_cache_free_entry */
1567 +void apc_cache_free_entry(apc_cache_entry_t* entry)
1568 +{
1569 + if (entry != NULL) {
1570 + assert(entry->ref_count == 0);
1571 + switch(entry->type) {
1572 + case APC_CACHE_ENTRY_FILE:
1573 + apc_sma_free(entry->data.file.filename);
1574 + apc_free_op_array(entry->data.file.op_array, apc_sma_free);
1575 + apc_free_functions(entry->data.file.functions, apc_sma_free);
1576 + apc_free_classes(entry->data.file.classes, apc_sma_free);
1577 + break;
1578 + case APC_CACHE_ENTRY_USER:
1579 + apc_sma_free(entry->data.user.info);
1580 + apc_cache_free_zval(entry->data.user.val, apc_sma_free);
1581 + break;
1582 + }
1583 + apc_sma_free(entry);
1584 + }
1585 +}
1586 +/* }}} */
1587 +
1588 +/* {{{ apc_cache_info */
1589 +apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited)
1590 +{
1591 + apc_cache_info_t* info;
1592 + slot_t* p;
1593 + int i;
1594 +
1595 + if(!cache) return NULL;
1596 +
1597 + LOCK(cache);
1598 +
1599 + info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t));
1600 + if(!info) {
1601 + UNLOCK(cache);
1602 + return NULL;
1603 + }
1604 + info->num_slots = cache->num_slots;
1605 + info->ttl = cache->ttl;
1606 + info->num_hits = cache->header->num_hits;
1607 + info->num_misses = cache->header->num_misses;
1608 + info->list = NULL;
1609 + info->deleted_list = NULL;
1610 + info->start_time = cache->header->start_time;
1611 + info->expunges = cache->header->expunges;
1612 + info->mem_size = cache->header->mem_size;
1613 + info->num_entries = cache->header->num_entries;
1614 + info->num_inserts = cache->header->num_inserts;
1615 +
1616 + if(!limited) {
1617 + /* For each hashtable slot */
1618 + for (i = 0; i < info->num_slots; i++) {
1619 + p = cache->slots[i];
1620 + for (; p != NULL; p = p->next) {
1621 + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1622 +
1623 + if(p->value->type == APC_CACHE_ENTRY_FILE) {
1624 + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1625 + link->data.file.device = p->key.data.file.device;
1626 + link->data.file.inode = p->key.data.file.inode;
1627 + link->type = APC_CACHE_ENTRY_FILE;
1628 + } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1629 + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1630 + link->data.user.ttl = p->value->data.user.ttl;
1631 + link->type = APC_CACHE_ENTRY_USER;
1632 + }
1633 + link->num_hits = p->num_hits;
1634 + link->mtime = p->key.mtime;
1635 + link->creation_time = p->creation_time;
1636 + link->deletion_time = p->deletion_time;
1637 + link->access_time = p->access_time;
1638 + link->ref_count = p->value->ref_count;
1639 + link->mem_size = p->value->mem_size;
1640 + link->next = info->list;
1641 + info->list = link;
1642 + }
1643 + }
1644 +
1645 + /* For each slot pending deletion */
1646 + for (p = cache->header->deleted_list; p != NULL; p = p->next) {
1647 + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1648 +
1649 + if(p->value->type == APC_CACHE_ENTRY_FILE) {
1650 + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1651 + if(p->key.type == APC_CACHE_KEY_FILE) {
1652 + link->data.file.device = p->key.data.file.device;
1653 + link->data.file.inode = p->key.data.file.inode;
1654 + } else { /* This is a no-stat fullpath file entry */
1655 + link->data.file.device = 0;
1656 + link->data.file.inode = 0;
1657 + }
1658 + link->type = APC_CACHE_ENTRY_FILE;
1659 + } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1660 + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1661 + link->data.user.ttl = p->value->data.user.ttl;
1662 + link->type = APC_CACHE_ENTRY_USER;
1663 + }
1664 + link->num_hits = p->num_hits;
1665 + link->mtime = p->key.mtime;
1666 + link->creation_time = p->creation_time;
1667 + link->deletion_time = p->deletion_time;
1668 + link->access_time = p->access_time;
1669 + link->ref_count = p->value->ref_count;
1670 + link->mem_size = p->value->mem_size;
1671 + link->next = info->deleted_list;
1672 + info->deleted_list = link;
1673 + }
1674 + }
1675 +
1676 + UNLOCK(cache);
1677 + return info;
1678 +}
1679 +/* }}} */
1680 +
1681 +/* {{{ apc_cache_free_info */
1682 +void apc_cache_free_info(apc_cache_info_t* info)
1683 +{
1684 + apc_cache_link_t* p = info->list;
1685 + apc_cache_link_t* q = NULL;
1686 + while (p != NULL) {
1687 + q = p;
1688 + p = p->next;
1689 + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1690 + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1691 + apc_efree(q);
1692 + }
1693 + p = info->deleted_list;
1694 + while (p != NULL) {
1695 + q = p;
1696 + p = p->next;
1697 + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1698 + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1699 + apc_efree(q);
1700 + }
1701 + apc_efree(info);
1702 +}
1703 +/* }}} */
1704 +
1705 +/* {{{ apc_cache_unlock */
1706 +void apc_cache_unlock(apc_cache_t* cache)
1707 +{
1708 + UNLOCK(cache);
1709 +}
1710 +/* }}} */
1711 +
1712 +/* {{{ apc_cache_busy */
1713 +zend_bool apc_cache_busy(apc_cache_t* cache)
1714 +{
1715 + return cache->header->busy;
1716 +}
1717 +/* }}} */
1718 +
1719 +#if NONBLOCKING_LOCK_AVAILABLE
1720 +/* {{{ apc_cache_write_lock */
1721 +zend_bool apc_cache_write_lock(apc_cache_t* cache)
1722 +{
1723 + return apc_lck_nb_lock(cache->header->wrlock);
1724 +}
1725 +/* }}} */
1726 +
1727 +/* {{{ apc_cache_write_unlock */
1728 +void apc_cache_write_unlock(apc_cache_t* cache)
1729 +{
1730 + apc_lck_unlock(cache->header->wrlock);
1731 +}
1732 +/* }}} */
1733 +#endif
1734 +
1735 +/* {{{ make_local_slot */
1736 +static local_slot_t* make_local_slot(apc_local_cache_t* cache, local_slot_t* lslot, slot_t* slot)
1737 +{
1738 + apc_cache_entry_t* value;
1739 +
1740 + value = apc_emalloc(sizeof(apc_cache_entry_t));
1741 + memcpy(value, slot->value, sizeof(apc_cache_entry_t)); /* bitwise copy */
1742 + value->local = 1;
1743 +
1744 + lslot->original = slot;
1745 + lslot->value = value;
1746 + lslot->num_hits++;
1747 +
1748 + return lslot; /* for what joy ? ... consistency */
1749 +}
1750 +/* }}} */
1751 +
1752 +/* {{{ free_local_slot */
1753 +static void free_local_slot(apc_local_cache_t* cache, local_slot_t* lslot)
1754 +{
1755 + local_slot_t * dead = NULL;
1756 + if(!lslot->original) return;
1757 +
1758 + /* TODO: Bad design to allocate memory in a free_* - fix when bored (hehe) */
1759 + dead = apc_emalloc(sizeof(local_slot_t));
1760 + memcpy(dead, lslot, sizeof(local_slot_t)); /* bitwise copy */
1761 +
1762 + lslot->original = NULL;
1763 + lslot->value = NULL;
1764 +
1765 + dead->next = cache->dead_list;
1766 + cache->dead_list = dead;
1767 +}
1768 +/* }}} */
1769 +
1770 +/* {{{ apc_local_cache_create */
1771 +apc_local_cache_t* apc_local_cache_create(apc_cache_t *shmcache, int num_slots, int ttl)
1772 +{
1773 + apc_local_cache_t* cache = NULL;
1774 +
1775 + cache = (apc_local_cache_t*) apc_emalloc(sizeof(apc_local_cache_t));
1776 +
1777 + cache->slots = (local_slot_t*) (apc_emalloc(sizeof(local_slot_t) * num_slots));
1778 + memset(cache->slots, 0, sizeof(local_slot_t) * num_slots);
1779 +
1780 + cache->shmcache = shmcache;
1781 + cache->num_slots = num_slots;
1782 + cache->ttl = ttl;
1783 + cache->num_hits = 0;
1784 + cache->generation = shmcache->header->expunges;
1785 + cache->dead_list = NULL;
1786 +
1787 + return cache;
1788 +}
1789 +/* }}} */
1790 +
1791 +/* {{{ apc_local_cache_cleanup */
1792 +void apc_local_cache_cleanup(apc_local_cache_t* cache) {
1793 + local_slot_t * lslot;
1794 + time_t t = time(0);
1795 +
1796 + int i;
1797 + for(i = 0; i < cache->num_slots; i++) {
1798 + slot_t * slot = cache->slots[i].original;
1799 + if((slot && slot->access_time < (t - cache->ttl)) ||
1800 + cache->generation != cache->shmcache->header->expunges) {
1801 + free_local_slot(cache, &cache->slots[i]);
1802 + }
1803 + }
1804 +
1805 + LOCK(cache->shmcache);
1806 + for(lslot = cache->dead_list; lslot != NULL; lslot = lslot->next) {
1807 + lslot->original->num_hits += lslot->num_hits;
1808 + lslot->original->value->ref_count--; /* apc_cache_release(cache->shmcache, lslot->original->value); */
1809 + apc_efree(lslot->value);
1810 + }
1811 + UNLOCK(cache->shmcache);
1812 +
1813 + cache->dead_list = NULL;
1814 +}
1815 +/* }}} */
1816 +
1817 +/* {{{ apc_local_cache_destroy */
1818 +void apc_local_cache_destroy(apc_local_cache_t* cache)
1819 +{
1820 + int i;
1821 + for(i = 0; i < cache->num_slots; i++) {
1822 + free_local_slot(cache, &cache->slots[i]);
1823 + }
1824 +
1825 + apc_local_cache_cleanup(cache);
1826 +
1827 + LOCK(cache->shmcache);
1828 + cache->shmcache->header->num_hits += cache->num_hits;
1829 + UNLOCK(cache->shmcache);
1830 +
1831 + apc_efree(cache->slots);
1832 + apc_efree(cache);
1833 +}
1834 +/* }}} */
1835 +
1836 +/* {{{ apc_local_cache_find */
1837 +apc_cache_entry_t* apc_local_cache_find(apc_local_cache_t* cache, apc_cache_key_t key, time_t t)
1838 +{
1839 + slot_t* slot;
1840 + local_slot_t* lslot;
1841 +
1842 + if(key.type == APC_CACHE_KEY_FILE) lslot = &cache->slots[hash(key) % cache->num_slots];
1843 + else lslot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
1844 +
1845 + slot = lslot->original;
1846 +
1847 + if(slot && key.type == slot->key.type) {
1848 + if(slot->access_time < (t - cache->ttl)) {
1849 + goto not_found;
1850 + }
1851 + if(key.type == APC_CACHE_KEY_FILE &&
1852 + key_equals(slot->key.data.file, key.data.file)) {
1853 + if(slot->key.mtime != key.mtime) {
1854 + free_local_slot(cache, lslot);
1855 + goto not_found;
1856 + }
1857 + return lslot->value;
1858 + } else if(key.type == APC_CACHE_KEY_FPFILE) {
1859 + if(!memcmp(slot->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
1860 + return lslot->value;
1861 + }
1862 + }
1863 + }
1864 +not_found:
1865 + if(apc_cache_busy(cache->shmcache)) {
1866 + return NULL;
1867 + }
1868 +
1869 + slot = apc_cache_find_slot(cache->shmcache, key, t);
1870 +
1871 + if(!slot) return NULL;
1872 +
1873 + /* i.e maintain a sort of top list */
1874 + if(lslot->original == NULL || (lslot->original->num_hits + lslot->num_hits) < slot->num_hits) {
1875 + free_local_slot(cache, lslot);
1876 + make_local_slot(cache, lslot, slot);
1877 + return lslot->value;
1878 + }
1879 + return slot->value;
1880 +}
1881 +/* }}} */
1882 +
1883 +/*
1884 + * Local variables:
1885 + * tab-width: 4
1886 + * c-basic-offset: 4
1887 + * End:
1888 + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
1889 + * vim<600: expandtab sw=4 ts=4 sts=4
1890 + */
1891 diff -ruN php-5.1.6.old/ext/apc/apc_cache.h php-5.1.6/ext/APC/apc_cache.h
1892 --- php-5.1.6.old/ext/apc/apc_cache.h 1969-12-31 18:00:00.000000000 -0600
1893 +++ php-5.1.6/ext/apc/apc_cache.h 2007-04-02 18:05:30.000000000 -0500
1894 @@ -0,0 +1,313 @@
1895 +/*
1896 + +----------------------------------------------------------------------+
1897 + | APC |
1898 + +----------------------------------------------------------------------+
1899 + | Copyright (c) 2006 The PHP Group |
1900 + +----------------------------------------------------------------------+
1901 + | This source file is subject to version 3.01 of the PHP license, |
1902 + | that is bundled with this package in the file LICENSE, and is |
1903 + | available through the world-wide-web at the following url: |
1904 + | http://www.php.net/license/3_01.txt. |
1905 + | If you did not receive a copy of the PHP license and are unable to |
1906 + | obtain it through the world-wide-web, please send a note to |
1907 + | license@php.net so we can mail you a copy immediately. |
1908 + +----------------------------------------------------------------------+
1909 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
1910 + | Rasmus Lerdorf <rasmus@php.net> |
1911 + +----------------------------------------------------------------------+
1912 +
1913 + This software was contributed to PHP by Community Connect Inc. in 2002
1914 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
1915 + Future revisions and derivatives of this source code must acknowledge
1916 + Community Connect Inc. as the original contributor of this module by
1917 + leaving this note intact in the source code.
1918 +
1919 + All other licensing and usage conditions are those of the PHP Group.
1920 +
1921 + */
1922 +
1923 +/* $Id: apc_cache.h,v 3.45 2007/03/22 16:03:59 gopalv Exp $ */
1924 +
1925 +#ifndef APC_CACHE_H
1926 +#define APC_CACHE_H
1927 +
1928 +/*
1929 + * This module defines the shared memory file cache. Basically all of the
1930 + * logic for storing and retrieving cache entries lives here.
1931 + */
1932 +
1933 +#include "apc.h"
1934 +#include "apc_compile.h"
1935 +
1936 +#define APC_CACHE_ENTRY_FILE 1
1937 +#define APC_CACHE_ENTRY_USER 2
1938 +
1939 +#define APC_CACHE_KEY_FILE 1
1940 +#define APC_CACHE_KEY_USER 2
1941 +#define APC_CACHE_KEY_FPFILE 3
1942 +
1943 +/* {{{ struct definition: apc_cache_key_t */
1944 +#define T apc_cache_t*
1945 +typedef struct apc_cache_t apc_cache_t; /* opaque cache type */
1946 +
1947 +typedef union _apc_cache_key_data_t {
1948 + struct {
1949 + dev_t device; /* the filesystem device */
1950 + ino_t inode; /* the filesystem inode */
1951 + } file;
1952 + struct {
1953 + const char *identifier;
1954 + int identifier_len;
1955 + } user;
1956 + struct {
1957 + const char *fullpath;
1958 + int fullpath_len;
1959 + } fpfile;
1960 +} apc_cache_key_data_t;
1961 +
1962 +typedef struct apc_cache_key_t apc_cache_key_t;
1963 +struct apc_cache_key_t {
1964 + apc_cache_key_data_t data;
1965 + time_t mtime; /* the mtime of this cached entry */
1966 + unsigned char type;
1967 +};
1968 +/* }}} */
1969 +
1970 +/* {{{ struct definition: apc_cache_entry_t */
1971 +typedef union _apc_cache_entry_value_t {
1972 + struct {
1973 + char *filename; /* absolute path to source file */
1974 + zend_op_array* op_array; /* op_array allocated in shared memory */
1975 + apc_function_t* functions; /* array of apc_function_t's */
1976 + apc_class_t* classes; /* array of apc_class_t's */
1977 + } file;
1978 + struct {
1979 + char *info;
1980 + int info_len;
1981 + zval *val;
1982 + unsigned int ttl;
1983 + } user;
1984 +} apc_cache_entry_value_t;
1985 +
1986 +typedef struct apc_cache_entry_t apc_cache_entry_t;
1987 +struct apc_cache_entry_t {
1988 + apc_cache_entry_value_t data;
1989 + unsigned char type;
1990 + unsigned char autofiltered;
1991 + unsigned char local;
1992 + int ref_count;
1993 + size_t mem_size;
1994 +};
1995 +/* }}} */
1996 +
1997 +/*
1998 + * apc_cache_create creates the shared memory compiler cache. This function
1999 + * should be called just once (ideally in the web server parent process, e.g.
2000 + * in apache), otherwise you will end up with multiple caches (which won't
2001 + * necessarily break anything). Returns a pointer to the cache object.
2002 + *
2003 + * size_hint is a "hint" at the total number of source files that will be
2004 + * cached. It determines the physical size of the hash table. Passing 0 for
2005 + * this argument will use a reasonable default value.
2006 + *
2007 + * gc_ttl is the maximum time a cache entry may speed on the garbage
2008 + * collection list. This is basically a work around for the inherent
2009 + * unreliability of our reference counting mechanism (see apc_cache_release).
2010 + *
2011 + * ttl is the maximum time a cache entry can idle in a slot in case the slot
2012 + * is needed. This helps in cleaning up the cache and ensuring that entries
2013 + * hit frequently stay cached and ones not hit very often eventually disappear.
2014 + */
2015 +extern T apc_cache_create(int size_hint, int gc_ttl, int ttl);
2016 +
2017 +/*
2018 + * apc_cache_destroy releases any OS resources associated with a cache object.
2019 + * Under apache, this function can be safely called by the child processes
2020 + * when they exit.
2021 + */
2022 +extern void apc_cache_destroy(T cache);
2023 +
2024 +/*
2025 + * apc_cache_clear empties a cache. This can safely be called at any time,
2026 + * even while other server processes are executing cached source files.
2027 + */
2028 +extern void apc_cache_clear(T cache);
2029 +
2030 +/*
2031 + * apc_cache_insert adds an entry to the cache, using a filename as a key.
2032 + * Internally, the filename is translated to a canonical representation, so
2033 + * that relative and absolute filenames will map to a single key. Returns
2034 + * non-zero if the file was successfully inserted, 0 otherwise. If 0 is
2035 + * returned, the caller must free the cache entry by calling
2036 + * apc_cache_free_entry (see below).
2037 + *
2038 + * key is the value created by apc_cache_make_file_key for file keys.
2039 + *
2040 + * value is a cache entry returned by apc_cache_make_entry (see below).
2041 + */
2042 +extern int apc_cache_insert(T cache, apc_cache_key_t key,
2043 + apc_cache_entry_t* value, time_t t);
2044 +
2045 +extern int apc_cache_user_insert(T cache, apc_cache_key_t key,
2046 + apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC);
2047 +
2048 +/*
2049 + * apc_cache_find searches for a cache entry by filename, and returns a
2050 + * pointer to the entry if found, NULL otherwise.
2051 + *
2052 + * key is a value created by apc_cache_make_file_key for file keys.
2053 + */
2054 +extern apc_cache_entry_t* apc_cache_find(T cache, apc_cache_key_t key, time_t t);
2055 +
2056 +/*
2057 + * apc_cache_user_find searches for a cache entry by its hashed identifier,
2058 + * and returns a pointer to the entry if found, NULL otherwise.
2059 + *
2060 + */
2061 +extern apc_cache_entry_t* apc_cache_user_find(T cache, char* strkey, int keylen, time_t t);
2062 +
2063 +/*
2064 + * apc_cache_user_delete finds an entry in the user cache and deletes it.
2065 + */
2066 +extern int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen);
2067 +
2068 +/* apc_cach_fetch_zval takes a zval in the cache and reconstructs a runtime
2069 + * zval from it.
2070 + *
2071 + */
2072 +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate);
2073 +
2074 +/*
2075 + * apc_cache_release decrements the reference count associated with a cache
2076 + * entry. Calling apc_cache_find automatically increments the reference count,
2077 + * and this function must be called post-execution to return the count to its
2078 + * original value. Failing to do so will prevent the entry from being
2079 + * garbage-collected.
2080 + *
2081 + * entry is the cache entry whose ref count you want to decrement.
2082 + */
2083 +extern void apc_cache_release(T cache, apc_cache_entry_t* entry);
2084 +
2085 +/*
2086 + * apc_cache_make_file_key creates a key object given a relative or absolute
2087 + * filename and an optional list of auxillary paths to search. include_path is
2088 + * searched if the filename cannot be found relative to the current working
2089 + * directory.
2090 + *
2091 + * key points to caller-allocated storage (must not be null).
2092 + *
2093 + * filename is the path to the source file.
2094 + *
2095 + * include_path is a colon-separated list of directories to search.
2096 + *
2097 + * and finally we pass in the current request time so we can avoid
2098 + * caching files with a current mtime which tends to indicate that
2099 + * they are still being written to.
2100 + */
2101 +extern int apc_cache_make_file_key(apc_cache_key_t* key,
2102 + const char* filename,
2103 + const char* include_path,
2104 + time_t t
2105 + TSRMLS_DC);
2106 +
2107 +/*
2108 + * apc_cache_make_file_entry creates an apc_cache_entry_t object given a filename
2109 + * and the compilation results returned by the PHP compiler.
2110 + */
2111 +extern apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
2112 + zend_op_array* op_array,
2113 + apc_function_t* functions,
2114 + apc_class_t* classes);
2115 +/*
2116 + * apc_cache_make_user_entry creates an apc_cache_entry_t object given an info string
2117 + * and the zval to be stored.
2118 + */
2119 +extern apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval *val, const unsigned int ttl);
2120 +
2121 +extern int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t);
2122 +
2123 +/*
2124 + * Frees all memory associated with an object returned by apc_cache_make_entry
2125 + * (see above).
2126 + */
2127 +extern void apc_cache_free_entry(apc_cache_entry_t* entry);
2128 +
2129 +/* {{{ struct definition: apc_cache_link_data_t */
2130 +typedef union _apc_cache_link_data_t {
2131 + struct {
2132 + char *filename;
2133 + dev_t device;
2134 + ino_t inode;
2135 + } file;
2136 + struct {
2137 + char *info;
2138 + unsigned int ttl;
2139 + } user;
2140 +} apc_cache_link_data_t;
2141 +/* }}} */
2142 +
2143 +/* {{{ struct definition: apc_cache_link_t */
2144 +typedef struct apc_cache_link_t apc_cache_link_t;
2145 +struct apc_cache_link_t {
2146 + apc_cache_link_data_t data;
2147 + unsigned char type;
2148 + int num_hits;
2149 + time_t mtime;
2150 + time_t creation_time;
2151 + time_t deletion_time;
2152 + time_t access_time;
2153 + int ref_count;
2154 + size_t mem_size;
2155 + apc_cache_link_t* next;
2156 +};
2157 +/* }}} */
2158 +
2159 +/* {{{ struct definition: apc_cache_info_t */
2160 +typedef struct apc_cache_info_t apc_cache_info_t;
2161 +struct apc_cache_info_t {
2162 + int num_slots;
2163 + int num_hits;
2164 + int num_misses;
2165 + int ttl;
2166 + apc_cache_link_t* list;
2167 + apc_cache_link_t* deleted_list;
2168 + time_t start_time;
2169 + int expunges;
2170 + int num_entries;
2171 + int num_inserts;
2172 + size_t mem_size;
2173 +};
2174 +/* }}} */
2175 +
2176 +extern apc_cache_info_t* apc_cache_info(T cache, zend_bool limited);
2177 +extern void apc_cache_free_info(apc_cache_info_t* info);
2178 +extern void apc_cache_expunge(apc_cache_t* cache, time_t t);
2179 +extern void apc_cache_unlock(apc_cache_t* cache);
2180 +extern zend_bool apc_cache_busy(apc_cache_t* cache);
2181 +extern zend_bool apc_cache_write_lock(apc_cache_t* cache);
2182 +extern void apc_cache_write_unlock(apc_cache_t* cache);
2183 +
2184 +/*
2185 + * Process local cache, which keeps a refcount hungry version of the slots
2186 + * for quick access without a lock - as long as the entry exists in local
2187 + * cache, the refcount of the shm version will be +1 more than required.
2188 + * It holds no data, only a shallow copy of apc_cache_entry.
2189 + */
2190 +typedef struct apc_local_cache_t apc_local_cache_t; /* process-local cache */
2191 +
2192 +extern apc_local_cache_t* apc_local_cache_create(apc_cache_t *shmcache, int num_slots, int ttl);
2193 +extern apc_cache_entry_t* apc_local_cache_find(apc_local_cache_t* cache, apc_cache_key_t key, time_t t);
2194 +extern void apc_local_cache_destroy(apc_local_cache_t* cache);
2195 +extern void apc_local_cache_cleanup(apc_local_cache_t* cache);
2196 +
2197 +#undef T
2198 +#endif
2199 +
2200 +/*
2201 + * Local variables:
2202 + * tab-width: 4
2203 + * c-basic-offset: 4
2204 + * End:
2205 + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
2206 + * vim<600: expandtab sw=4 ts=4 sts=4
2207 + */
2208 diff -ruN php-5.1.6.old/ext/apc/apc_compile.c php-5.1.6/ext/APC/apc_compile.c
2209 --- php-5.1.6.old/ext/apc/apc_compile.c 1969-12-31 18:00:00.000000000 -0600
2210 +++ php-5.1.6/ext/apc/apc_compile.c 2007-04-02 18:05:30.000000000 -0500
2211 @@ -0,0 +1,2530 @@
2212 +/*
2213 + +----------------------------------------------------------------------+
2214 + | APC |
2215 + +----------------------------------------------------------------------+
2216 + | Copyright (c) 2006 The PHP Group |
2217 + +----------------------------------------------------------------------+
2218 + | This source file is subject to version 3.01 of the PHP license, |
2219 + | that is bundled with this package in the file LICENSE, and is |
2220 + | available through the world-wide-web at the following url: |
2221 + | http://www.php.net/license/3_01.txt. |
2222 + | If you did not receive a copy of the PHP license and are unable to |
2223 + | obtain it through the world-wide-web, please send a note to |
2224 + | license@php.net so we can mail you a copy immediately. |
2225 + +----------------------------------------------------------------------+
2226 + | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
2227 + | Rasmus Lerdorf <rasmus@php.net> |
2228 + | Arun C. Murthy <arunc@yahoo-inc.com> |
2229 + | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
2230 + +----------------------------------------------------------------------+
2231 +
2232 + This software was contributed to PHP by Community Connect Inc. in 2002
2233 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
2234 + Future revisions and derivatives of this source code must acknowledge
2235 + Community Connect Inc. as the original contributor of this module by
2236 + leaving this note intact in the source code.
2237 +
2238 + All other licensing and usage conditions are those of the PHP Group.
2239 +
2240 + */
2241 +
2242 +/* $Id: apc_compile.c,v 3.85 2007/03/28 07:35:55 gopalv Exp $ */
2243 +
2244 +#include "apc_compile.h"
2245 +#include "apc_globals.h"
2246 +#include "apc_zend.h"
2247 +
2248 +typedef void* (*ht_copy_fun_t)(void*, void*, apc_malloc_t, apc_free_t);
2249 +typedef void (*ht_free_fun_t)(void*, apc_free_t);
2250 +typedef int (*ht_check_copy_fun_t)(Bucket*, va_list);
2251 +
2252 +#ifdef ZEND_ENGINE_2
2253 +typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*);
2254 +#endif
2255 +
2256 +#define CHECK(p) { if ((p) == NULL) return NULL; }
2257 +
2258 +/* {{{ internal function declarations */
2259 +
2260 +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size);
2261 +
2262 +static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_malloc_t);
2263 +
2264 +/*
2265 + * The "copy" functions perform deep-copies on a particular data structure
2266 + * (passed as the second argument). They also optionally allocate space for
2267 + * the destination data structure if the first argument is null.
2268 + */
2269 +static zval** my_copy_zval_ptr(zval**, const zval**, apc_malloc_t, apc_free_t);
2270 +static zval* my_copy_zval(zval*, const zval*, apc_malloc_t, apc_free_t);
2271 +static znode* my_copy_znode(znode*, znode*, apc_malloc_t, apc_free_t);
2272 +static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_malloc_t, apc_free_t);
2273 +static zend_function* my_copy_function(zend_function*, zend_function*, apc_malloc_t, apc_free_t);
2274 +static zend_function_entry* my_copy_function_entry(zend_function_entry*, zend_function_entry*, apc_malloc_t, apc_free_t);
2275 +static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_malloc_t, apc_free_t);
2276 +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, ...);
2277 +#define my_copy_hashtable( dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate) \
2278 + my_copy_hashtable_ex(dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate, NULL)
2279 +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate);
2280 +#ifdef ZEND_ENGINE_2
2281 +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate);
2282 +static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, zend_arg_info*, uint, apc_malloc_t, apc_free_t);
2283 +static zend_arg_info* my_copy_arg_info(zend_arg_info*, zend_arg_info*, apc_malloc_t, apc_free_t);
2284 +#endif
2285 +/*
2286 + * The "destroy" functions free the memory associated with a particular data
2287 + * structure but do not free the pointer to the data structure.
2288 + *
2289 + * my_destroy_zval() returns SUCCESS or FAILURE, FAILURE means that
2290 + * the zval* has other references elsewhere
2291 + */
2292 +static int my_destroy_zval(zval*, apc_free_t);
2293 +static void my_destroy_zval_ptr(zval**, apc_free_t);
2294 +static void my_destroy_zend_op(zend_op*, apc_free_t);
2295 +static void my_destroy_znode(znode*, apc_free_t);
2296 +static void my_destroy_function(zend_function*, apc_free_t);
2297 +static void my_destroy_function_entry(zend_function_entry*, apc_free_t);
2298 +static void my_destroy_class_entry(zend_class_entry*, apc_free_t);
2299 +static void my_destroy_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
2300 +static void my_destroy_op_array(zend_op_array*, apc_free_t);
2301 +#ifdef ZEND_ENGINE_2
2302 +static void my_destroy_property_info(zend_property_info*, apc_free_t);
2303 +static void my_destroy_arg_info_array(zend_arg_info* src, uint, apc_free_t);
2304 +static void my_destroy_arg_info(zend_arg_info*, apc_free_t);
2305 +#endif
2306 +
2307 +/*
2308 + * The "free" functions work exactly like their "destroy" counterparts (see
2309 + * above) but also free the pointer to the data structure.
2310 + */
2311 +static void my_free_zval_ptr(zval**, apc_free_t);
2312 +static void my_free_function(zend_function*, apc_free_t);
2313 +static void my_free_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
2314 +#ifdef ZEND_ENGINE_2
2315 +static void my_free_property_info(zend_property_info* src, apc_free_t);
2316 +static void my_free_arg_info_array(zend_arg_info*, uint, apc_free_t);
2317 +static void my_free_arg_info(zend_arg_info*, apc_free_t);
2318 +#endif
2319 +
2320 +/*
2321 + * The "fixup" functions need for ZEND_ENGINE_2
2322 + */
2323 +#ifdef ZEND_ENGINE_2
2324 +static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
2325 +static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst );
2326 +/* my_fixup_function_for_execution is the same as my_fixup_function
2327 + * but named differently for clarity
2328 + */
2329 +#define my_fixup_function_for_execution my_fixup_function
2330 +
2331 +#ifdef ZEND_ENGINE_2_2
2332 +static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
2333 +#define my_fixup_property_info_for_execution my_fixup_property_info
2334 +#endif
2335 +
2336 +#endif
2337 +
2338 +/*
2339 + * These functions return "1" if the member/function is
2340 + * defined/overridden in the 'current' class and not inherited.
2341 + */
2342 +static int my_check_copy_function(Bucket* src, va_list args);
2343 +static int my_check_copy_default_property(Bucket* p, va_list args);
2344 +#ifdef ZEND_ENGINE_2
2345 +static int my_check_copy_property_info(Bucket* src, va_list args);
2346 +static int my_check_copy_static_member(Bucket* src, va_list args);
2347 +#endif
2348 +
2349 +/* }}} */
2350 +
2351 +/* {{{ check_op_array_integrity */
2352 +#if 0
2353 +static void check_op_array_integrity(zend_op_array* src)
2354 +{
2355 + int i, j;
2356 +
2357 + /* These sorts of checks really aren't particularly effective, but they
2358 + * can provide a welcome sanity check when debugging. Just don't enable
2359 + * for production use! */
2360 +
2361 + assert(src->refcount != NULL);
2362 + assert(src->opcodes != NULL);
2363 + assert(src->last > 0);
2364 +
2365 + for (i = 0; i < src->last; i++) {
2366 + zend_op* op = &src->opcodes[i];
2367 + znode* nodes[] = { &op->result, &op->op1, &op->op2 };
2368 + for (j = 0; j < 3; j++) {
2369 + assert(nodes[j]->op_type == IS_CONST ||
2370 + nodes[j]->op_type == IS_VAR ||
2371 + nodes[j]->op_type == IS_TMP_VAR ||
2372 + nodes[j]->op_type == IS_UNUSED);
2373 +
2374 + if (nodes[j]->op_type == IS_CONST) {
2375 + int type = nodes[j]->u.constant.type;
2376 + assert(type == IS_RESOURCE ||
2377 + type == IS_BOOL ||
2378 + type == IS_LONG ||
2379 + type == IS_DOUBLE ||
2380 + type == IS_NULL ||
2381 + type == IS_CONSTANT ||
2382 + type == IS_STRING ||
2383 + type == FLAG_IS_BC ||
2384 + type == IS_ARRAY ||
2385 + type == IS_CONSTANT_ARRAY ||
2386 + type == IS_OBJECT);
2387 + }
2388 + }
2389 + }
2390 +}
2391 +#endif
2392 +/* }}} */
2393 +
2394 +/* {{{ is_derived_class */
2395 +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size)
2396 +{
2397 + int i;
2398 +
2399 + /*
2400 + * Scan the op_array for execution-time class declarations of derived
2401 + * classes. If we find one whose key matches our current class key, we
2402 + * know the current class is a derived class.
2403 + *
2404 + * This check is exceedingly inefficient (fortunately it only has to occur
2405 + * once, when the source file is first compiled and cached), but the
2406 + * compiler should save this information for us -- definitely a candidate
2407 + * for a Zend Engine patch.
2408 + *
2409 + * XXX checking for derived classes provides a minimal (albeit measurable)
2410 + * speed up. It may not be worth the added complexity -- considere
2411 + * removing this optimization.
2412 + */
2413 +
2414 + for (i = 0; i < op_array->last; i++) {
2415 + zend_op* op = &op_array->opcodes[i];
2416 +
2417 +#ifdef ZEND_ENGINE_2
2418 + if (op->opcode == ZEND_DECLARE_CLASS &&
2419 + op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
2420 +#else
2421 + if (op->opcode == ZEND_DECLARE_FUNCTION_OR_CLASS &&
2422 + op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
2423 +#endif
2424 + {
2425 + if (op->op1.u.constant.value.str.len == key_size &&
2426 + !memcmp(op->op1.u.constant.value.str.val, key, key_size))
2427 + {
2428 + return 1;
2429 + }
2430 + }
2431 + }
2432 +
2433 + return 0;
2434 +}
2435 +/* }}} */
2436 +
2437 +/* {{{ my_bitwise_copy_function */
2438 +static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate)
2439 +{
2440 + assert(src != NULL);
2441 +
2442 + if (!dst) {
2443 + CHECK(dst = (zend_function*) allocate(sizeof(src[0])));
2444 + }
2445 +
2446 + /* We only need to do a bitwise copy */
2447 + memcpy(dst, src, sizeof(src[0]));
2448 +
2449 + return dst;
2450 +}
2451 +/* }}} */
2452 +
2453 +/* {{{ my_copy_zval_ptr */
2454 +static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_malloc_t allocate, apc_free_t deallocate)
2455 +{
2456 + int local_dst_alloc = 0;
2457 + zval* dst_new;
2458 +
2459 + assert(src != NULL);
2460 +
2461 + if (!dst) {
2462 + CHECK(dst = (zval**) allocate(sizeof(zval*)));
2463 + local_dst_alloc = 1;
2464 + }
2465 +
2466 + if(!(dst[0] = (zval*) allocate(sizeof(zval)))) {
2467 + if(local_dst_alloc) deallocate(dst);
2468 + return NULL;
2469 + }
2470 + dst_new = my_copy_zval(*dst, *src, allocate, deallocate);
2471 + if(dst_new != *dst) {
2472 + deallocate(*dst);
2473 + *dst = dst_new;
2474 + }
2475 +
2476 + (*dst)->refcount = (*src)->refcount;
2477 + (*dst)->is_ref = (*src)->is_ref;
2478 +
2479 + return dst;
2480 +}
2481 +/* }}} */
2482 +
2483 +/* {{{ my_copy_zval */
2484 +static zval* my_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
2485 +{
2486 + zval **tmp;
2487 + TSRMLS_FETCH();
2488 +
2489 + assert(dst != NULL);
2490 + assert(src != NULL);
2491 +
2492 + memcpy(dst, src, sizeof(src[0]));
2493 +
2494 + switch (src->type & ~IS_CONSTANT_INDEX) {
2495 + case IS_RESOURCE:
2496 + case IS_BOOL:
2497 + case IS_LONG:
2498 + case IS_DOUBLE:
2499 + case IS_NULL:
2500 + break;
2501 +
2502 + case IS_CONSTANT:
2503 + case IS_STRING:
2504 +#ifndef ZEND_ENGINE_2
2505 + case FLAG_IS_BC:
2506 +#endif
2507 + if (src->value.str.val) {
2508 + CHECK(dst->value.str.val = apc_xmemcpy(src->value.str.val,
2509 + src->value.str.len+1,
2510 + allocate));
2511 + }
2512 + break;
2513 +
2514 + case IS_ARRAY:
2515 +
2516 + if(APCG(copied_zvals)) {
2517 + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
2518 + (*tmp)->refcount++;
2519 + return *tmp;
2520 + }
2521 +
2522 + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL);
2523 + }
2524 + /* fall through */
2525 +
2526 + case IS_CONSTANT_ARRAY:
2527 +
2528 + CHECK(dst->value.ht =
2529 + my_copy_hashtable(NULL,
2530 + src->value.ht,
2531 + (ht_copy_fun_t) my_copy_zval_ptr,
2532 + (ht_free_fun_t) my_free_zval_ptr,
2533 + 1,
2534 + allocate, deallocate));
2535 + break;
2536 +
2537 + case IS_OBJECT:
2538 +#ifndef ZEND_ENGINE_2
2539 + CHECK(dst->value.obj.ce =
2540 + my_copy_class_entry(NULL, src->value.obj.ce, allocate, deallocate));
2541 +
2542 + if(!(dst->value.obj.properties = my_copy_hashtable(NULL,
2543 + src->value.obj.properties,
2544 + (ht_copy_fun_t) my_copy_zval_ptr,
2545 + (ht_free_fun_t) my_free_zval_ptr,
2546 + 1,
2547 + allocate, deallocate))) {
2548 + my_destroy_class_entry(dst->value.obj.ce, deallocate);
2549 + return NULL;
2550 + }
2551 + break;
2552 +#else
2553 + dst->type = IS_NULL;
2554 +#endif
2555 + break;
2556 +
2557 + default:
2558 + assert(0);
2559 + }
2560 +
2561 + return dst;
2562 +}
2563 +/* }}} */
2564 +
2565 +/* {{{ my_copy_znode */
2566 +static znode* my_copy_znode(znode* dst, znode* src, apc_malloc_t allocate, apc_free_t deallocate)
2567 +{
2568 + assert(dst != NULL);
2569 + assert(src != NULL);
2570 +
2571 + memcpy(dst, src, sizeof(src[0]));
2572 +
2573 +#ifdef IS_CV
2574 + assert(dst ->op_type == IS_CONST ||
2575 + dst ->op_type == IS_VAR ||
2576 + dst ->op_type == IS_CV ||
2577 + dst ->op_type == IS_TMP_VAR ||
2578 + dst ->op_type == IS_UNUSED);
2579 +#else
2580 + assert(dst ->op_type == IS_CONST ||
2581 + dst ->op_type == IS_VAR ||
2582 + dst ->op_type == IS_TMP_VAR ||
2583 + dst ->op_type == IS_UNUSED);
2584 +#endif
2585 +
2586 + if (src->op_type == IS_CONST) {
2587 + if(!my_copy_zval(&dst->u.constant, &src->u.constant, allocate, deallocate)) {
2588 + return NULL;
2589 + }
2590 + }
2591 +
2592 + return dst;
2593 +}
2594 +/* }}} */
2595 +
2596 +/* {{{ my_copy_zend_op */
2597 +static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_malloc_t allocate, apc_free_t deallocate)
2598 +{
2599 + assert(dst != NULL);
2600 + assert(src != NULL);
2601 +
2602 + memcpy(dst, src, sizeof(src[0]));
2603 +
2604 + if( my_copy_znode(&dst->result, &src->result, allocate, deallocate) == NULL
2605 + || my_copy_znode(&dst->op1, &src->op1, allocate, deallocate) == NULL
2606 + || my_copy_znode(&dst->op2, &src->op2, allocate, deallocate) == NULL)
2607 + {
2608 + return NULL;
2609 + }
2610 +
2611 + return dst;
2612 +}
2613 +/* }}} */
2614 +
2615 +/* {{{ my_copy_function */
2616 +static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
2617 +{
2618 + int local_dst_alloc = 0;
2619 + TSRMLS_FETCH();
2620 +
2621 + assert(src != NULL);
2622 +
2623 + if(!dst) local_dst_alloc = 1;
2624 + CHECK(dst = my_bitwise_copy_function(dst, src, allocate));
2625 +
2626 + switch (src->type) {
2627 + case ZEND_INTERNAL_FUNCTION:
2628 + case ZEND_OVERLOADED_FUNCTION:
2629 + /* shallow copy because op_array is internal */
2630 + dst->op_array = src->op_array;
2631 + break;
2632 +
2633 + case ZEND_USER_FUNCTION:
2634 + case ZEND_EVAL_CODE:
2635 + if(!apc_copy_op_array(&dst->op_array,
2636 + &src->op_array,
2637 + allocate, deallocate TSRMLS_CC)) {
2638 + if(local_dst_alloc) deallocate(dst);
2639 + return NULL;
2640 + }
2641 + break;
2642 +
2643 + default:
2644 + assert(0);
2645 + }
2646 +#ifdef ZEND_ENGINE_2
2647 + /*
2648 + * op_array bitwise copying overwrites what ever you modified
2649 + * before apc_copy_op_array - which is why this code is outside
2650 + * my_bitwise_copy_function.
2651 + */
2652 +
2653 + /* zend_do_inheritance will re-look this up, because the pointers
2654 + * in prototype are from a function table of another class. It just
2655 + * helps if that one is from EG(class_table).
2656 + */
2657 + dst->common.prototype = NULL;
2658 +
2659 + /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you
2660 + * have to carry around a prototype. Thankfully zend_do_inheritance
2661 + * sets this properly as well
2662 + */
2663 + dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT);
2664 +#endif
2665 +
2666 +
2667 + return dst;
2668 +}
2669 +/* }}} */
2670 +
2671 +/* {{{ my_copy_function_entry */
2672 +static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, zend_function_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
2673 +{
2674 + int local_dst_alloc = 0;
2675 + assert(src != NULL);
2676 +
2677 + if (!dst) {
2678 + CHECK(dst = (zend_function_entry*) allocate(sizeof(src[0])));
2679 + local_dst_alloc = 1;
2680 + }
2681 +
2682 + /* Start with a bitwise copy */
2683 + memcpy(dst, src, sizeof(src[0]));
2684 +
2685 + dst->fname = NULL;
2686 +#ifdef ZEND_ENGINE_2
2687 + dst->arg_info = NULL;
2688 +#else
2689 + dst->func_arg_types = NULL;
2690 +#endif
2691 +
2692 + if (src->fname) {
2693 + if(!(dst->fname = apc_xstrdup(src->fname, allocate))) {
2694 + goto cleanup;
2695 + }
2696 + }
2697 +
2698 +#ifdef ZEND_ENGINE_2
2699 + if (src->arg_info) {
2700 + if(!(dst->arg_info = my_copy_arg_info_array(NULL,
2701 + src->arg_info,
2702 + src->num_args,
2703 + allocate,
2704 + deallocate))) {
2705 + goto cleanup;
2706 + }
2707 + }
2708 +#else
2709 + if (src->func_arg_types) {
2710 + if(!(dst->func_arg_types = apc_xmemcpy(src->func_arg_types,
2711 + src->func_arg_types[0]+1,
2712 + allocate))) {
2713 + goto cleanup;
2714 + }
2715 + }
2716 +#endif
2717 +
2718 + return dst;
2719 +
2720 +cleanup:
2721 + if(dst->fname) deallocate(dst->fname);
2722 + if(local_dst_alloc) deallocate(dst);
2723 + return NULL;
2724 +}
2725 +/* }}} */
2726 +
2727 +#ifdef ZEND_ENGINE_2
2728 +/* {{{ my_copy_property_info */
2729 +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
2730 +{
2731 + int local_dst_alloc = 0;
2732 +
2733 + assert(src != NULL);
2734 +
2735 + if (!dst) {
2736 + CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
2737 + local_dst_alloc = 1;
2738 + }
2739 +
2740 + /* Start with a bitwise copy */
2741 + memcpy(dst, src, sizeof(*src));
2742 +
2743 + dst->name = NULL;
2744 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2745 + dst->doc_comment = NULL;
2746 +#endif
2747 +
2748 + if (src->name) {
2749 + /* private members are stored inside property_info as a mangled
2750 + * string of the form:
2751 + * \0<classname>\0<membername>\0
2752 + */
2753 + if(!(dst->name =
2754 + apc_xmemcpy(src->name, src->name_length+1, allocate))) {
2755 + goto cleanup;
2756 + }
2757 + }
2758 +
2759 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2760 + if (src->doc_comment) {
2761 + if( !(dst->doc_comment =
2762 + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
2763 + goto cleanup;
2764 + }
2765 + }
2766 +#endif
2767 +
2768 + return dst;
2769 +
2770 +cleanup:
2771 + if(dst->name) deallocate(dst->name);
2772 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
2773 + if(dst->doc_comment) deallocate(dst->doc_comment);
2774 +#endif
2775 + if(local_dst_alloc) deallocate(dst);
2776 + return NULL;
2777 +}
2778 +/* }}} */
2779 +
2780 +/* {{{ my_copy_property_info_for_execution */
2781 +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)
2782 +{
2783 + int local_dst_alloc = 0;
2784 +
2785 + assert(src != NULL);
2786 +
2787 + if (!dst) {
2788 + CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
2789 + local_dst_alloc = 1;
2790 + }
2791 +
2792 + /* We need only a shallow copy */
2793 + memcpy(dst, src, sizeof(*src));
2794 +
2795 + return dst;
2796 +}
2797 +/* }}} */
2798 +
2799 +/* {{{ my_copy_arg_info_array */
2800 +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)
2801 +{
2802 + int local_dst_alloc = 0;
2803 + int i = 0;
2804 +
2805 +
2806 + if (!dst) {
2807 + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)*num_args));
2808 + local_dst_alloc = 1;
2809 + }
2810 +
2811 + /* Start with a bitwise copy */
2812 + memcpy(dst, src, sizeof(*src)*num_args);
2813 +
2814 + for(i=0; i < num_args; i++) {
2815 + if(!(my_copy_arg_info( &dst[i], &src[i], allocate, deallocate))) {
2816 + if(i) my_destroy_arg_info_array(dst, i-1, deallocate);
2817 + if(local_dst_alloc) deallocate(dst);
2818 + return NULL;
2819 + }
2820 + }
2821 +
2822 + return dst;
2823 +}
2824 +/* }}} */
2825 +
2826 +/* {{{ my_copy_arg_info */
2827 +static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, zend_arg_info* src, apc_malloc_t allocate, apc_free_t deallocate)
2828 +{
2829 + int local_dst_alloc = 0;
2830 +
2831 + assert(src != NULL);
2832 +
2833 + if (!dst) {
2834 + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)));
2835 + local_dst_alloc = 1;
2836 + }
2837 +
2838 + /* Start with a bitwise copy */
2839 + memcpy(dst, src, sizeof(*src));
2840 +
2841 + dst->name = NULL;
2842 + dst->class_name = NULL;
2843 +
2844 + if (src->name) {
2845 + if(!(dst->name =
2846 + apc_xmemcpy(src->name, src->name_len+1, allocate))) {
2847 + goto cleanup;
2848 + }
2849 + }
2850 +
2851 + if (src->class_name) {
2852 + if(!(dst->class_name =
2853 + apc_xmemcpy(src->class_name, src->class_name_len+1, allocate))) {
2854 + goto cleanup;
2855 + }
2856 + }
2857 +
2858 + return dst;
2859 +
2860 +cleanup:
2861 + if(dst->name) deallocate(dst->name);
2862 + if(dst->class_name) deallocate(dst->name);
2863 + if(local_dst_alloc) deallocate(dst);
2864 + return NULL;
2865 +}
2866 +/* }}} */
2867 +#endif
2868 +
2869 +/* {{{ my_copy_class_entry */
2870 +static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
2871 +{
2872 + int local_dst_alloc = 0;
2873 + int i = 0;
2874 +
2875 + assert(src != NULL);
2876 +
2877 + if (!dst) {
2878 + CHECK(dst = (zend_class_entry*) allocate(sizeof(*src)));
2879 + local_dst_alloc = 1;
2880 + }
2881 +
2882 + /* Start with a bitwise copy */
2883 + memcpy(dst, src, sizeof(*src));
2884 +
2885 + dst->name = NULL;
2886 + dst->builtin_functions = NULL;
2887 + memset(&dst->function_table, 0, sizeof(dst->function_table));
2888 + memset(&dst->default_properties, 0, sizeof(dst->default_properties));
2889 +#ifndef ZEND_ENGINE_2
2890 + dst->refcount = NULL;
2891 +#else
2892 + dst->static_members = NULL;
2893 + dst->doc_comment = NULL;
2894 + dst->filename = NULL;
2895 + memset(&dst->properties_info, 0, sizeof(dst->properties_info));
2896 + memset(&dst->constants_table, 0, sizeof(dst->constants_table));
2897 + memset(&dst->default_static_members, 0, sizeof(dst->default_static_members));
2898 +#endif
2899 +
2900 + if (src->name) {
2901 + if(!(dst->name = apc_xstrdup(src->name, allocate))) {
2902 + goto cleanup;
2903 + }
2904 + }
2905 +
2906 +#ifndef ZEND_ENGINE_2
2907 + if(!(dst->refcount = apc_xmemcpy(src->refcount,
2908 + sizeof(src->refcount[0]),
2909 + allocate))) {
2910 + goto cleanup;
2911 + }
2912 +#endif
2913 +
2914 + if(!(my_copy_hashtable_ex(&dst->function_table,
2915 + &src->function_table,
2916 + (ht_copy_fun_t) my_copy_function,
2917 + (ht_free_fun_t) my_free_function,
2918 + 0,
2919 + allocate, deallocate,
2920 + (ht_check_copy_fun_t) my_check_copy_function,
2921 + src))) {
2922 + goto cleanup;
2923 + }
2924 +
2925 +#ifdef ZEND_ENGINE_2
2926 +
2927 + /* the interfaces are populated at runtime using ADD_INTERFACE */
2928 + dst->interfaces = NULL;
2929 +
2930 + /* the current count includes inherited interfaces as well,
2931 + the real dynamic ones are the first <n> which are zero'd
2932 + out in zend_do_end_class_declaration */
2933 + for(i = 0 ; i < src->num_interfaces ; i++) {
2934 + if(src->interfaces[i])
2935 + {
2936 + dst->num_interfaces = i;
2937 + break;
2938 + }
2939 + }
2940 +
2941 + /* these will either be set inside my_fixup_hashtable or
2942 + * they will be copied out from parent inside zend_do_inheritance
2943 + */
2944 + dst->constructor = NULL;
2945 + dst->destructor = NULL;
2946 + dst->clone = NULL;
2947 + dst->__get = NULL;
2948 + dst->__set = NULL;
2949 + dst->__unset = NULL;
2950 + dst->__isset = NULL;
2951 + dst->__call = NULL;
2952 +#ifdef ZEND_ENGINE_2_2
2953 + dst->__tostring = NULL;
2954 +#endif
2955 +
2956 + /* unset function proxies */
2957 + dst->serialize_func = NULL;
2958 + dst->unserialize_func = NULL;
2959 +
2960 + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst);
2961 +#endif
2962 +
2963 + if(!(my_copy_hashtable_ex(&dst->default_properties,
2964 + &src->default_properties,
2965 + (ht_copy_fun_t) my_copy_zval_ptr,
2966 + (ht_free_fun_t) my_free_zval_ptr,
2967 + 1,
2968 + allocate,deallocate,
2969 + (ht_check_copy_fun_t) my_check_copy_default_property,
2970 + src))) {
2971 + goto cleanup;
2972 + }
2973 +
2974 +#ifdef ZEND_ENGINE_2
2975 +
2976 + if(!(my_copy_hashtable_ex(&dst->properties_info,
2977 + &src->properties_info,
2978 + (ht_copy_fun_t) my_copy_property_info,
2979 + (ht_free_fun_t) my_free_property_info,
2980 + 0,
2981 + allocate, deallocate,
2982 + (ht_check_copy_fun_t) my_check_copy_property_info,
2983 + src))) {
2984 + goto cleanup;
2985 + }
2986 +
2987 +#ifdef ZEND_ENGINE_2_2
2988 + /* php5.2 introduced a scope attribute for property info */
2989 + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
2990 +#endif
2991 +
2992 + if(!my_copy_hashtable_ex(&dst->default_static_members,
2993 + &src->default_static_members,
2994 + (ht_copy_fun_t) my_copy_zval_ptr,
2995 + (ht_free_fun_t) my_free_zval_ptr,
2996 + 1,
2997 + allocate, deallocate,
2998 + (ht_check_copy_fun_t) my_check_copy_static_member,
2999 + src,
3000 + &src->default_static_members)) {
3001 + goto cleanup;
3002 + }
3003 + if(src->static_members != &src->default_static_members)
3004 + {
3005 + if(!(dst->static_members = my_copy_hashtable_ex(NULL,
3006 + src->static_members,
3007 + (ht_copy_fun_t) my_copy_zval_ptr,
3008 + (ht_free_fun_t) my_free_zval_ptr,
3009 + 1,
3010 + allocate, deallocate,
3011 + (ht_check_copy_fun_t) my_check_copy_static_member,
3012 + src,
3013 + src->static_members))) {
3014 + goto cleanup;
3015 + }
3016 + }
3017 + else
3018 + {
3019 + dst->static_members = &dst->default_static_members;
3020 + }
3021 +
3022 + if(!(my_copy_hashtable(&dst->constants_table,
3023 + &src->constants_table,
3024 + (ht_copy_fun_t) my_copy_zval_ptr,
3025 + (ht_free_fun_t) my_free_zval_ptr,
3026 + 1,
3027 + allocate, deallocate))) {
3028 + goto cleanup;
3029 + }
3030 +
3031 + if (src->doc_comment) {
3032 + if(!(dst->doc_comment =
3033 + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
3034 + goto cleanup;
3035 + }
3036 + }
3037 +#endif
3038 +
3039 + if (src->builtin_functions) {
3040 + int i, n;
3041 +
3042 + for (n = 0; src->type == ZEND_INTERNAL_CLASS && src->builtin_functions[n].fname != NULL; n++) {}
3043 +
3044 + if(!(dst->builtin_functions =
3045 + (zend_function_entry*)
3046 + allocate((n + 1) * sizeof(zend_function_entry)))) {
3047 + goto cleanup;
3048 + }
3049 +
3050 +
3051 + for (i = 0; i < n; i++) {
3052 + if(!my_copy_function_entry(&dst->builtin_functions[i],
3053 + &src->builtin_functions[i],
3054 + allocate, deallocate)) {
3055 + int ii;
3056 +
3057 + for(ii=i-1; i>=0; i--) my_destroy_function_entry(&dst->builtin_functions[ii], deallocate);
3058 + goto cleanup;
3059 + }
3060 + }
3061 + dst->builtin_functions[n].fname = NULL;
3062 + }
3063 +
3064 +#ifdef ZEND_ENGINE_2
3065 + if (src->filename) {
3066 + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
3067 + goto cleanup;
3068 + }
3069 + }
3070 +#endif
3071 +
3072 + return dst;
3073 +
3074 +
3075 +cleanup:
3076 + if(dst->name) deallocate(dst->name);
3077 +#ifdef ZEND_ENGINE_2
3078 + if(dst->doc_comment) deallocate(dst->doc_comment);
3079 + if(dst->filename) deallocate(dst->filename);
3080 +#else
3081 + if(dst->refcount) deallocate(dst->refcount);
3082 +#endif
3083 +
3084 + if(dst->builtin_functions) deallocate(dst->builtin_functions);
3085 + if(dst->function_table.arBuckets) my_destroy_hashtable(&dst->function_table, (ht_free_fun_t) my_free_function, deallocate);
3086 + if(dst->default_properties.arBuckets) my_destroy_hashtable(&dst->default_properties, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3087 +
3088 +#ifdef ZEND_ENGINE_2
3089 + if(dst->properties_info.arBuckets) my_destroy_hashtable(&dst->properties_info, (ht_free_fun_t) my_free_property_info, deallocate);
3090 + if(dst->default_static_members.arBuckets)
3091 + {
3092 + my_destroy_hashtable(&dst->default_static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3093 + }
3094 + if(dst->static_members && dst->static_members != &(dst->default_static_members))
3095 + {
3096 + my_destroy_hashtable(dst->static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3097 + deallocate(dst->static_members);
3098 + }
3099 + if(dst->constants_table.arBuckets) my_destroy_hashtable(&dst->constants_table, (ht_free_fun_t) my_free_zval_ptr, deallocate);
3100 +#endif
3101 + if(local_dst_alloc) deallocate(dst);
3102 +
3103 + return NULL;
3104 +}
3105 +/* }}} */
3106 +
3107 +/* {{{ my_copy_hashtable */
3108 +static HashTable* my_copy_hashtable_ex(HashTable* dst,
3109 + HashTable* src,
3110 + ht_copy_fun_t copy_fn,
3111 + ht_free_fun_t free_fn,
3112 + int holds_ptrs,
3113 + apc_malloc_t allocate,
3114 + apc_free_t deallocate,
3115 + ht_check_copy_fun_t check_fn,
3116 + ...)
3117 +{
3118 + Bucket* curr = NULL;
3119 + Bucket* prev = NULL;
3120 + Bucket* newp = NULL;
3121 + int first = 1;
3122 + int local_dst_alloc = 0;
3123 + int index = 0;
3124 +
3125 + assert(src != NULL);
3126 +
3127 + if (!dst) {
3128 + CHECK(dst = (HashTable*) allocate(sizeof(src[0])));
3129 + local_dst_alloc = 1;
3130 + }
3131 +
3132 + memcpy(dst, src, sizeof(src[0]));
3133 +
3134 + /* allocate buckets for the new hashtable */
3135 + if(!(dst->arBuckets = allocate(dst->nTableSize * sizeof(Bucket*)))) {
3136 + if(local_dst_alloc) deallocate(dst);
3137 + return NULL;
3138 + }
3139 +
3140 + memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
3141 + dst->pInternalPointer = NULL;
3142 + dst->pListHead = NULL;
3143 +
3144 + for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
3145 + int n = curr->h % dst->nTableSize;
3146 +
3147 + if(check_fn) {
3148 + va_list args;
3149 + va_start(args, check_fn);
3150 +
3151 + /* Call the check_fn to see if the current bucket
3152 + * needs to be copied out
3153 + */
3154 + if(!check_fn(curr, args)) {
3155 + dst->nNumOfElements--;
3156 + continue;
3157 + }
3158 +
3159 + va_end(args);
3160 + }
3161 +
3162 + /* create a copy of the bucket 'curr' */
3163 + if(!(newp =
3164 + (Bucket*) apc_xmemcpy(curr,
3165 + sizeof(Bucket) + curr->nKeyLength - 1,
3166 + allocate))) {
3167 + goto cleanup;
3168 + }
3169 +
3170 + /* insert 'newp' into the linked list at its hashed index */
3171 + if (dst->arBuckets[n]) {
3172 + newp->pNext = dst->arBuckets[n];
3173 + newp->pLast = NULL;
3174 + newp->pNext->pLast = newp;
3175 + }
3176 + else {
3177 + newp->pNext = newp->pLast = NULL;
3178 + }
3179 +
3180 + dst->arBuckets[n] = newp;
3181 +
3182 + /* copy the bucket data using our 'copy_fn' callback function */
3183 + if(!(newp->pData = copy_fn(NULL, curr->pData, allocate, deallocate))) {
3184 + goto cleanup;
3185 + }
3186 +
3187 + if (holds_ptrs) {
3188 + memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
3189 + }
3190 + else {
3191 + newp->pDataPtr = NULL;
3192 + }
3193 +
3194 + /* insert 'newp' into the table-thread linked list */
3195 + newp->pListLast = prev;
3196 + newp->pListNext = NULL;
3197 +
3198 + if (prev) {
3199 + prev->pListNext = newp;
3200 + }
3201 +
3202 + if (first) {
3203 + dst->pListHead = newp;
3204 + first = 0;
3205 + }
3206 +
3207 + prev = newp;
3208 + }
3209 +
3210 + dst->pListTail = newp;
3211 +
3212 + return dst;
3213 +
3214 + cleanup:
3215 + for(index = 0; index < dst->nTableSize; index++)
3216 + {
3217 + curr = dst->arBuckets[index];
3218 + while(curr != NULL)
3219 + {
3220 + Bucket * tmp = curr;
3221 + if(curr->pData && free_fn)
3222 + {
3223 + free_fn(curr->pData, deallocate);
3224 + }
3225 + curr = curr->pNext;
3226 + deallocate(tmp);
3227 + }
3228 + }
3229 + deallocate(dst->arBuckets);
3230 + if(local_dst_alloc) deallocate(dst);
3231 + else dst->arBuckets = NULL;
3232 +
3233 + return NULL;
3234 +}
3235 +/* }}} */
3236 +
3237 +/* {{{ my_copy_static_variables */
3238 +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate)
3239 +{
3240 + if (src->static_variables == NULL) {
3241 + return NULL;
3242 + }
3243 +
3244 + return my_copy_hashtable(NULL,
3245 + src->static_variables,
3246 + (ht_copy_fun_t) my_copy_zval_ptr,
3247 + (ht_free_fun_t) my_free_zval_ptr,
3248 + 1,
3249 + allocate, deallocate);
3250 +}
3251 +/* }}} */
3252 +
3253 +/* {{{ apc_copy_zval */
3254 +zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
3255 +{
3256 + int local_dst_alloc = 0;
3257 + assert(src != NULL);
3258 +
3259 + if (!dst) {
3260 + CHECK(dst = (zval*) allocate(sizeof(zval)));
3261 + local_dst_alloc = 1;
3262 + }
3263 +
3264 + dst = my_copy_zval(dst, src, allocate, deallocate);
3265 + if(!dst) {
3266 + if(local_dst_alloc) deallocate(dst);
3267 + return NULL;
3268 + }
3269 + return dst;
3270 +}
3271 +/* }}} */
3272 +
3273 +#ifdef ZEND_ENGINE_2
3274 +/* {{{ apc_fixup_op_array_jumps */
3275 +static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src )
3276 +{
3277 + int i;
3278 +
3279 + for (i=0; i < dst->last; ++i) {
3280 + zend_op *zo = &(dst->opcodes[i]);
3281 + /*convert opline number to jump address*/
3282 + switch (zo->opcode) {
3283 + case ZEND_JMP:
3284 + /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/
3285 + zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes);
3286 + break;
3287 + case ZEND_JMPZ:
3288 + case ZEND_JMPNZ:
3289 + case ZEND_JMPZ_EX:
3290 + case ZEND_JMPNZ_EX:
3291 + zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes);
3292 + break;
3293 + default:
3294 + break;
3295 + }
3296 + }
3297 +}
3298 +/* }}} */
3299 +#endif
3300 +
3301 +/* {{{ apc_copy_op_array */
3302 +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)
3303 +{
3304 + int i;
3305 + int local_dst_alloc = 0;
3306 + apc_fileinfo_t fileinfo;
3307 + char canon_path[MAXPATHLEN];
3308 + char *fullpath = NULL;
3309 +#ifdef ZEND_ENGINE_2
3310 + apc_opflags_t * flags = NULL;
3311 +#endif
3312 +
3313 + assert(src != NULL);
3314 +
3315 + if (!dst) {
3316 + CHECK(dst = (zend_op_array*) allocate(sizeof(src[0])));
3317 + local_dst_alloc = 1;
3318 + }
3319 +
3320 + if(APCG(apc_optimize_function)) {
3321 + APCG(apc_optimize_function)(src TSRMLS_CC);
3322 + }
3323 +
3324 + /* start with a bitwise copy of the array */
3325 + memcpy(dst, src, sizeof(src[0]));
3326 +
3327 + dst->function_name = NULL;
3328 + dst->filename = NULL;
3329 + dst->refcount = NULL;
3330 + dst->opcodes = NULL;
3331 + dst->brk_cont_array = NULL;
3332 + dst->static_variables = NULL;
3333 +#ifdef ZEND_ENGINE_2
3334 + dst->try_catch_array = NULL;
3335 + dst->arg_info = NULL;
3336 + dst->doc_comment = NULL;
3337 +#else
3338 + dst->arg_types = NULL;
3339 +#endif
3340 +#ifdef ZEND_ENGINE_2_1
3341 + dst->vars = NULL;
3342 +#endif
3343 +
3344 + /* copy the arg types array (if set) */
3345 +#ifdef ZEND_ENGINE_2
3346 + if (src->arg_info) {
3347 + if(!(dst->arg_info = my_copy_arg_info_array(NULL,
3348 + src->arg_info,
3349 + src->num_args,
3350 + allocate,
3351 + deallocate))) {
3352 + goto cleanup;
3353 + }
3354 + }
3355 +#else
3356 + if (src->arg_types) {
3357 + if(!(dst->arg_types = apc_xmemcpy(src->arg_types,
3358 + sizeof(src->arg_types[0]) * (src->arg_types[0]+1),
3359 + allocate))) {
3360 + goto cleanup;
3361 + }
3362 + }
3363 +#endif
3364 +
3365 + if (src->function_name) {
3366 + if(!(dst->function_name = apc_xstrdup(src->function_name, allocate))) {
3367 + goto cleanup;
3368 + }
3369 + }
3370 + if (src->filename) {
3371 + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
3372 + goto cleanup;
3373 + }
3374 + }
3375 +
3376 + if(!(dst->refcount = apc_xmemcpy(src->refcount,
3377 + sizeof(src->refcount[0]),
3378 + allocate))) {
3379 + goto cleanup;
3380 + }
3381 +
3382 + /* deep-copy the opcodes */
3383 + if(!(dst->opcodes = (zend_op*) allocate(sizeof(zend_op) * src->last))) {
3384 + goto cleanup;
3385 + }
3386 +
3387 +#ifdef ZEND_ENGINE_2
3388 + if(APCG(reserved_offset) != -1) {
3389 + /* Insanity alert: the void* pointer is cast into an apc_opflags_t
3390 + * struct. apc_zend_init() checks to ensure that it fits in a void* */
3391 + flags = (apc_opflags_t*) & (dst->reserved[APCG(reserved_offset)]);
3392 + memset(flags, 0, sizeof(apc_opflags_t));
3393 + /* assert(sizeof(apc_opflags_t) < sizeof(dst->reserved)); */
3394 + }
3395 +#endif
3396 +
3397 + for (i = 0; i < src->last; i++) {
3398 +#ifdef ZEND_ENGINE_2
3399 + zend_op *zo = &(src->opcodes[i]);
3400 + /* a lot of files are merely constant arrays with no jumps */
3401 + switch (zo->opcode) {
3402 + case ZEND_JMP:
3403 + case ZEND_JMPZ:
3404 + case ZEND_JMPNZ:
3405 + case ZEND_JMPZ_EX:
3406 + case ZEND_JMPNZ_EX:
3407 + if(flags != NULL) {
3408 + flags->has_jumps = 1;
3409 + }
3410 + break;
3411 +#ifdef ZEND_ENGINE_2
3412 + /* auto_globals_jit was not in php-4.3.* */
3413 + case ZEND_FETCH_R:
3414 + case ZEND_FETCH_W:
3415 + case ZEND_FETCH_IS:
3416 + case ZEND_FETCH_FUNC_ARG:
3417 + if(PG(auto_globals_jit) && flags != NULL)
3418 + {
3419 + /* The fetch is only required if auto_globals_jit=1 */
3420 + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL &&
3421 + zo->op1.op_type == IS_CONST &&
3422 + zo->op1.u.constant.type == IS_STRING) {
3423 + znode * varname = &zo->op1;
3424 + if (varname->u.constant.value.str.val[0] == '_') {
3425 +#define SET_IF_AUTOGLOBAL(member) \
3426 + if(!strcmp(varname->u.constant.value.str.val, #member)) \
3427 + flags->member = 1 /* no ';' here */
3428 + SET_IF_AUTOGLOBAL(_GET);
3429 + else SET_IF_AUTOGLOBAL(_POST);
3430 + else SET_IF_AUTOGLOBAL(_COOKIE);
3431 + else SET_IF_AUTOGLOBAL(_SERVER);
3432 + else SET_IF_AUTOGLOBAL(_ENV);
3433 + else SET_IF_AUTOGLOBAL(_FILES);
3434 + else SET_IF_AUTOGLOBAL(_REQUEST);
3435 + else if(zend_is_auto_global(
3436 + varname->u.constant.value.str.val,
3437 + varname->u.constant.value.str.len
3438 + TSRMLS_CC))
3439 + {
3440 + flags->unknown_global = 1;
3441 + }
3442 + }
3443 + }
3444 + }
3445 + break;
3446 +#endif
3447 + case ZEND_RECV_INIT:
3448 + if(zo->op2.op_type == IS_CONST &&
3449 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY) {
3450 + if(flags != NULL) {
3451 + flags->deep_copy = 1;
3452 + }
3453 + }
3454 + break;
3455 + default:
3456 + if((zo->op1.op_type == IS_CONST &&
3457 + zo->op1.u.constant.type == IS_CONSTANT_ARRAY) ||
3458 + (zo->op2.op_type == IS_CONST &&
3459 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) {
3460 + if(flags != NULL) {
3461 + flags->deep_copy = 1;
3462 + }
3463 + }
3464 + break;
3465 + }
3466 +#endif
3467 + if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, allocate, deallocate))) {
3468 + int ii;
3469 + for(ii = i-1; ii>=0; ii--) {
3470 + my_destroy_zend_op(dst->opcodes+ii, deallocate);
3471 + }
3472 + goto cleanup;
3473 + }
3474 +#ifdef ZEND_ENGINE_2
3475 +/* This code breaks apc's rule#1 - cache what you compile */
3476 + if(APCG(fpstat)==0) {
3477 + if((zo->opcode == ZEND_INCLUDE_OR_EVAL) &&
3478 + (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) {
3479 + /* constant includes */
3480 + if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),len)) {
3481 + if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), &fileinfo) == 0) {
3482 + if((IS_ABSOLUTE_PATH(fileinfo.fullpath, strlen(fileinfo.fullpath)) && (fullpath = fileinfo.fullpath))
3483 + || (fullpath = realpath(fileinfo.fullpath, canon_path))) {
3484 + /* is either an absolute path or it goes through a realpath() */
3485 + zend_op *dzo = &(dst->opcodes[i]);
3486 + deallocate(dzo->op1.u.constant.value.str.val);
3487 + dzo->op1.u.constant.value.str.len = strlen(fullpath);
3488 + dzo->op1.u.constant.value.str.val = apc_xstrdup(fullpath, allocate);
3489 + }
3490 + }
3491 + }
3492 + }
3493 + }
3494 +#endif
3495 + }
3496 +
3497 +#ifdef ZEND_ENGINE_2
3498 + if(flags == NULL || flags->has_jumps) {
3499 + apc_fixup_op_array_jumps(dst,src);
3500 + }
3501 +#endif
3502 +
3503 + /* copy the break-continue array */
3504 + if (src->brk_cont_array) {
3505 + if(!(dst->brk_cont_array =
3506 + apc_xmemcpy(src->brk_cont_array,
3507 + sizeof(src->brk_cont_array[0]) * src->last_brk_cont,
3508 + allocate))) {
3509 + goto cleanup_opcodes;
3510 + }
3511 + }
3512 +
3513 + /* copy the table of static variables */
3514 + if (src->static_variables) {
3515 + if(!(dst->static_variables = my_copy_static_variables(src, allocate, deallocate))) {
3516 + goto cleanup_opcodes;
3517 + }
3518 + }
3519 +
3520 +#ifdef ZEND_ENGINE_2
3521 + if (src->try_catch_array) {
3522 + if(!(dst->try_catch_array =
3523 + apc_xmemcpy(src->try_catch_array,
3524 + sizeof(src->try_catch_array[0]) * src->last_try_catch,
3525 + allocate))) {
3526 + goto cleanup_opcodes;
3527 + }
3528 + }
3529 +#endif
3530 +
3531 +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */
3532 + if (src->vars) {
3533 + if(!(dst->vars = apc_xmemcpy(src->vars,
3534 + sizeof(src->vars[0]) * src->last_var,
3535 + allocate))) {
3536 + goto cleanup_opcodes;
3537 + }
3538 +
3539 + for(i = 0; i < src->last_var; i++) dst->vars[i].name = NULL;
3540 +
3541 + for(i = 0; i < src->last_var; i++) {
3542 + if(!(dst->vars[i].name = apc_xmemcpy(src->vars[i].name,
3543 + src->vars[i].name_len + 1,
3544 + allocate))) {
3545 + dst->last_var = i;
3546 + goto cleanup_opcodes;
3547 + }
3548 + }
3549 + }
3550 +#endif
3551 +
3552 +#ifdef ZEND_ENGINE_2
3553 + if (src->doc_comment) {
3554 + if (!(dst->doc_comment
3555 + = apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
3556 + goto cleanup_opcodes;
3557 + }
3558 + }
3559 +#endif
3560 +
3561 + return dst;
3562 +
3563 +cleanup_opcodes:
3564 + if(dst->opcodes) {
3565 + for(i=0; i < src->last; i++) my_destroy_zend_op(dst->opcodes+i, deallocate);
3566 + }
3567 +cleanup:
3568 + if(dst->function_name) deallocate(dst->function_name);
3569 + if(dst->refcount) deallocate(dst->refcount);
3570 + if(dst->filename) deallocate(dst->filename);
3571 +#ifdef ZEND_ENGINE_2
3572 + if(dst->arg_info) my_free_arg_info_array(dst->arg_info, dst->num_args, deallocate);
3573 + if(dst->try_catch_array) deallocate(dst->try_catch_array);
3574 + if(dst->doc_comment) deallocate(dst->doc_comment);
3575 +#else
3576 + if(dst->arg_types) deallocate(dst->arg_types);
3577 +#endif
3578 + if(dst->opcodes) deallocate(dst->opcodes);
3579 + if(dst->brk_cont_array) deallocate(dst->brk_cont_array);
3580 + if(dst->static_variables) my_free_hashtable(dst->static_variables, (ht_free_fun_t)my_free_zval_ptr, (apc_free_t)deallocate);
3581 +#ifdef ZEND_ENGINE_2_1
3582 + if (dst->vars) {
3583 + for(i=0; i < dst->last_var; i++) {
3584 + if(dst->vars[i].name) deallocate(dst->vars[i].name);
3585 + }
3586 + deallocate(dst->vars);
3587 + }
3588 +#endif
3589 + if(local_dst_alloc) deallocate(dst);
3590 + return NULL;
3591 +}
3592 +/* }}} */
3593 +
3594 +/* {{{ apc_copy_new_functions */
3595 +apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
3596 +{
3597 + apc_function_t* array;
3598 + int new_count; /* number of new functions in table */
3599 + int i;
3600 +
3601 + new_count = zend_hash_num_elements(CG(function_table)) - old_count;
3602 + assert(new_count >= 0);
3603 +
3604 + CHECK(array =
3605 + (apc_function_t*)
3606 + allocate(sizeof(apc_function_t) * (new_count+1)));
3607 +
3608 + if (new_count == 0) {
3609 + array[0].function = NULL;
3610 + return array;
3611 + }
3612 +
3613 + /* Skip the first `old_count` functions in the table */
3614 + zend_hash_internal_pointer_reset(CG(function_table));
3615 + for (i = 0; i < old_count; i++) {
3616 + zend_hash_move_forward(CG(function_table));
3617 + }
3618 +
3619 + /* Add the next `new_count` functions to our array */
3620 + for (i = 0; i < new_count; i++) {
3621 + char* key;
3622 + uint key_size;
3623 + zend_function* fun;
3624 +
3625 + zend_hash_get_current_key_ex(CG(function_table),
3626 + &key,
3627 + &key_size,
3628 + NULL,
3629 + 0,
3630 + NULL);
3631 +
3632 + zend_hash_get_current_data(CG(function_table), (void**) &fun);
3633 +
3634 + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
3635 + int ii;
3636 + for(ii=i-1; ii>=0; ii--) {
3637 + deallocate(array[ii].name);
3638 + my_free_function(array[ii].function, deallocate);
3639 + }
3640 + deallocate(array);
3641 + return NULL;
3642 + }
3643 + array[i].name_len = (int) key_size-1;
3644 + if(!(array[i].function = my_copy_function(NULL, fun, allocate, deallocate))) {
3645 + int ii;
3646 + deallocate(array[i].name);
3647 + for(ii=i-1; ii>=0; ii--) {
3648 + deallocate(array[ii].name);
3649 + my_free_function(array[ii].function, deallocate);
3650 + }
3651 + deallocate(array);
3652 + return NULL;
3653 + }
3654 + zend_hash_move_forward(CG(function_table));
3655 + }
3656 +
3657 + array[i].function = NULL;
3658 + return array;
3659 +}
3660 +/* }}} */
3661 +
3662 +/* {{{ apc_copy_new_classes */
3663 +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)
3664 +{
3665 + apc_class_t* array;
3666 + int new_count; /* number of new classes in table */
3667 + int i;
3668 +
3669 + new_count = zend_hash_num_elements(CG(class_table)) - old_count;
3670 + assert(new_count >= 0);
3671 +
3672 + CHECK(array =
3673 + (apc_class_t*)
3674 + allocate(sizeof(apc_class_t)*(new_count+1)));
3675 +
3676 + if (new_count == 0) {
3677 + array[0].class_entry = NULL;
3678 + return array;
3679 + }
3680 +
3681 + /* Skip the first `old_count` classes in the table */
3682 + zend_hash_internal_pointer_reset(CG(class_table));
3683 + for (i = 0; i < old_count; i++) {
3684 + zend_hash_move_forward(CG(class_table));
3685 + }
3686 +
3687 + /* Add the next `new_count` classes to our array */
3688 + for (i = 0; i < new_count; i++) {
3689 + char* key;
3690 + uint key_size;
3691 + zend_class_entry* elem = NULL;
3692 +
3693 + array[i].class_entry = NULL;
3694 +
3695 + zend_hash_get_current_key_ex(CG(class_table),
3696 + &key,
3697 + &key_size,
3698 + NULL,
3699 + 0,
3700 + NULL);
3701 +
3702 + zend_hash_get_current_data(CG(class_table), (void**) &elem);
3703 +
3704 +
3705 +#ifdef ZEND_ENGINE_2
3706 + elem = *((zend_class_entry**)elem);
3707 +#endif
3708 +
3709 + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
3710 + int ii;
3711 +
3712 + for(ii=i-1; ii>=0; ii--) {
3713 + deallocate(array[ii].name);
3714 + my_destroy_class_entry(array[ii].class_entry, deallocate);
3715 + deallocate(array[ii].class_entry);
3716 + }
3717 + deallocate(array);
3718 + return NULL;
3719 + }
3720 + array[i].name_len = (int) key_size-1;
3721 + if(!(array[i].class_entry = my_copy_class_entry(NULL, elem, allocate, deallocate))) {
3722 + int ii;
3723 +
3724 + deallocate(array[i].name);
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 +
3734 + /*
3735 + * If the class has a pointer to its parent class, save the parent
3736 + * name so that we can enable compile-time inheritance when we reload
3737 + * the child class; otherwise, set the parent name to null and scan
3738 + * the op_array to determine if this class inherits from some base
3739 + * class at execution-time.
3740 + */
3741 +
3742 + if (elem->parent) {
3743 + if(!(array[i].parent_name =
3744 + apc_xstrdup(elem->parent->name, allocate))) {
3745 + int ii;
3746 +
3747 + for(ii=i; ii>=0; ii--) {
3748 + deallocate(array[ii].name);
3749 + my_destroy_class_entry(array[ii].class_entry, deallocate);
3750 + deallocate(array[ii].class_entry);
3751 + if(ii==i) continue;
3752 + if(array[ii].parent_name) deallocate(array[ii].parent_name);
3753 + }
3754 + deallocate(array);
3755 + return NULL;
3756 + }
3757 + array[i].is_derived = 1;
3758 + }
3759 + else {
3760 + array[i].parent_name = NULL;
3761 + array[i].is_derived = is_derived_class(op_array, key, key_size);
3762 + }
3763 +
3764 + zend_hash_move_forward(CG(class_table));
3765 + }
3766 +
3767 + array[i].class_entry = NULL;
3768 + return array;
3769 +}
3770 +/* }}} */
3771 +
3772 +/* {{{ my_destroy_zval_ptr */
3773 +static void my_destroy_zval_ptr(zval** src, apc_free_t deallocate)
3774 +{
3775 + assert(src != NULL);
3776 + if(my_destroy_zval(src[0], deallocate) == SUCCESS) {
3777 + deallocate(src[0]);
3778 + }
3779 +}
3780 +/* }}} */
3781 +
3782 +/* {{{ my_destroy_zval */
3783 +static int my_destroy_zval(zval* src, apc_free_t deallocate)
3784 +{
3785 + zval **tmp;
3786 + TSRMLS_FETCH();
3787 +
3788 + switch (src->type & ~IS_CONSTANT_INDEX) {
3789 + case IS_RESOURCE:
3790 + case IS_BOOL:
3791 + case IS_LONG:
3792 + case IS_DOUBLE:
3793 + case IS_NULL:
3794 + break;
3795 +
3796 + case IS_CONSTANT:
3797 + case IS_STRING:
3798 +#ifndef ZEND_ENGINE_2
3799 + case FLAG_IS_BC:
3800 +#endif
3801 + deallocate(src->value.str.val);
3802 + break;
3803 +
3804 + case IS_ARRAY:
3805 +
3806 + /* Maintain a list of zvals we've copied to properly handle recursive structures */
3807 + if(APCG(copied_zvals)) {
3808 + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
3809 + (*tmp)->refcount--;
3810 + return FAILURE;
3811 + }
3812 + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&src, sizeof(zval*), NULL);
3813 + }
3814 + /* fall through */
3815 +
3816 + case IS_CONSTANT_ARRAY:
3817 + my_free_hashtable(src->value.ht,
3818 + (ht_free_fun_t) my_free_zval_ptr,
3819 + deallocate);
3820 + break;
3821 +
3822 + case IS_OBJECT:
3823 +#ifndef ZEND_ENGINE_2
3824 + my_destroy_class_entry(src->value.obj.ce, deallocate);
3825 + deallocate(src->value.obj.ce);
3826 + my_free_hashtable(src->value.obj.properties,
3827 + (ht_free_fun_t) my_free_zval_ptr,
3828 + deallocate);
3829 +#endif
3830 + break;
3831 +
3832 + default:
3833 + assert(0);
3834 + }
3835 +
3836 + return SUCCESS;
3837 +}
3838 +/* }}} */
3839 +
3840 +/* {{{ my_destroy_znode */
3841 +static void my_destroy_znode(znode* src, apc_free_t deallocate)
3842 +{
3843 + if (src->op_type == IS_CONST) {
3844 + my_destroy_zval(&src->u.constant, deallocate);
3845 + }
3846 +}
3847 +/* }}} */
3848 +
3849 +/* {{{ my_destroy_zend_op */
3850 +static void my_destroy_zend_op(zend_op* src, apc_free_t deallocate)
3851 +{
3852 + my_destroy_znode(&src->result, deallocate);
3853 + my_destroy_znode(&src->op1, deallocate);
3854 + my_destroy_znode(&src->op2, deallocate);
3855 +}
3856 +/* }}} */
3857 +
3858 +/* {{{ my_destroy_function */
3859 +static void my_destroy_function(zend_function* src, apc_free_t deallocate)
3860 +{
3861 + assert(src != NULL);
3862 +
3863 + switch (src->type) {
3864 + case ZEND_INTERNAL_FUNCTION:
3865 + case ZEND_OVERLOADED_FUNCTION:
3866 + break;
3867 +
3868 + case ZEND_USER_FUNCTION:
3869 + case ZEND_EVAL_CODE:
3870 + my_destroy_op_array(&src->op_array, deallocate);
3871 + break;
3872 +
3873 + default:
3874 + assert(0);
3875 + }
3876 +}
3877 +/* }}} */
3878 +
3879 +/* {{{ my_destroy_function_entry */
3880 +static void my_destroy_function_entry(zend_function_entry* src, apc_free_t deallocate)
3881 +{
3882 + assert(src != NULL);
3883 +
3884 + deallocate(src->fname);
3885 +#ifdef ZEND_ENGINE_2
3886 + if (src->arg_info) {
3887 + my_free_arg_info_array(src->arg_info, src->num_args, deallocate);
3888 + }
3889 +#else
3890 + if (src->func_arg_types) {
3891 + deallocate(src->func_arg_types);
3892 + }
3893 +#endif
3894 +}
3895 +/* }}} */
3896 +
3897 +#ifdef ZEND_ENGINE_2
3898 +/* {{{ my_destroy_property_info*/
3899 +static void my_destroy_property_info(zend_property_info* src, apc_free_t deallocate)
3900 +{
3901 + assert(src != NULL);
3902 +
3903 + deallocate(src->name);
3904 +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
3905 + if(src->doc_comment) deallocate(src->doc_comment);
3906 +#endif
3907 +}
3908 +/* }}} */
3909 +
3910 +/* {{{ my_destroy_arg_info_array */
3911 +static void my_destroy_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
3912 +{
3913 + int i = 0;
3914 +
3915 + assert(src != NULL);
3916 +
3917 + for(i=0; i < num_args; i++) {
3918 + my_destroy_arg_info(&src[i], deallocate);
3919 + }
3920 +}
3921 +/* }}} */
3922 +
3923 +/* {{{ my_destroy_arg_info */
3924 +static void my_destroy_arg_info(zend_arg_info* src, apc_free_t deallocate)
3925 +{
3926 + assert(src != NULL);
3927 +
3928 + deallocate(src->name);
3929 + deallocate(src->class_name);
3930 +}
3931 +/* }}} */
3932 +#endif
3933 +
3934 +/* {{{ my_destroy_class_entry */
3935 +static void my_destroy_class_entry(zend_class_entry* src, apc_free_t deallocate)
3936 +{
3937 + uint i;
3938 +
3939 + assert(src != NULL);
3940 +
3941 + deallocate(src->name);
3942 +#ifndef ZEND_ENGINE_2
3943 + deallocate(src->refcount);
3944 +#else
3945 + if(src->doc_comment) deallocate(src->doc_comment);
3946 + if(src->filename) deallocate(src->filename);
3947 +#endif
3948 +
3949 + my_destroy_hashtable(&src->function_table,
3950 + (ht_free_fun_t) my_free_function,
3951 + deallocate);
3952 +
3953 + my_destroy_hashtable(&src->default_properties,
3954 + (ht_free_fun_t) my_free_zval_ptr,
3955 + deallocate);
3956 +
3957 +#ifdef ZEND_ENGINE_2
3958 + my_destroy_hashtable(&src->properties_info,
3959 + (ht_free_fun_t) my_free_property_info,
3960 + deallocate);
3961 + if(src->static_members)
3962 + {
3963 + my_destroy_hashtable(src->static_members,
3964 + (ht_free_fun_t) my_free_zval_ptr,
3965 + deallocate);
3966 + if(src->static_members != &(src->default_static_members))
3967 + {
3968 + deallocate(src->static_members);
3969 + }
3970 + }
3971 +
3972 + my_destroy_hashtable(&src->constants_table,
3973 + (ht_free_fun_t) my_free_zval_ptr,
3974 + deallocate);
3975 +#endif
3976 +
3977 + if (src->builtin_functions) {
3978 + for (i = 0; src->builtin_functions[i].fname != NULL; i++) {
3979 + my_destroy_function_entry(&src->builtin_functions[i], deallocate);
3980 + }
3981 + deallocate(src->builtin_functions);
3982 + }
3983 +}
3984 +/* }}} */
3985 +
3986 +/* {{{ my_destroy_hashtable */
3987 +static void my_destroy_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
3988 +{
3989 + int i;
3990 +
3991 + assert(src != NULL);
3992 +
3993 + for (i = 0; i < src->nTableSize; i++) {
3994 + Bucket* p = src->arBuckets[i];
3995 + while (p != NULL) {
3996 + Bucket* q = p;
3997 + p = p->pNext;
3998 + free_fn(q->pData, deallocate);
3999 + deallocate(q);
4000 + }
4001 + }
4002 +
4003 + deallocate(src->arBuckets);
4004 +}
4005 +/* }}} */
4006 +
4007 +/* {{{ my_destroy_op_array */
4008 +static void my_destroy_op_array(zend_op_array* src, apc_free_t deallocate)
4009 +{
4010 + int i;
4011 +
4012 + assert(src != NULL);
4013 +
4014 +#ifdef ZEND_ENGINE_2
4015 + if (src->arg_info) {
4016 + my_free_arg_info_array(src->arg_info, src->num_args, deallocate);
4017 + }
4018 +#else
4019 + if (src->arg_types) {
4020 + deallocate(src->arg_types);
4021 + }
4022 +#endif
4023 +
4024 + deallocate(src->function_name);
4025 + deallocate(src->filename);
4026 + deallocate(src->refcount);
4027 +
4028 + for (i = 0; i < src->last; i++) {
4029 + my_destroy_zend_op(src->opcodes + i, deallocate);
4030 + }
4031 + deallocate(src->opcodes);
4032 +
4033 + if (src->brk_cont_array) {
4034 + deallocate(src->brk_cont_array);
4035 + }
4036 +
4037 + if (src->static_variables) {
4038 + my_free_hashtable(src->static_variables,
4039 + (ht_free_fun_t) my_free_zval_ptr,
4040 + deallocate);
4041 + }
4042 +
4043 +#ifdef ZEND_ENGINE_2_1
4044 + if (src->vars) {
4045 + for(i=0; i < src->last_var; i++) {
4046 + if(src->vars[i].name) deallocate(src->vars[i].name);
4047 + }
4048 + deallocate(src->vars);
4049 + }
4050 +#endif
4051 +#ifdef ZEND_ENGINE_2
4052 + if(src->try_catch_array) {
4053 + deallocate(src->try_catch_array);
4054 + }
4055 + if (src->doc_comment) {
4056 + deallocate(src->doc_comment);
4057 + }
4058 +#endif
4059 +}
4060 +/* }}} */
4061 +
4062 +/* {{{ my_free_zval_ptr */
4063 +static void my_free_zval_ptr(zval** src, apc_free_t deallocate)
4064 +{
4065 + my_destroy_zval_ptr(src, deallocate);
4066 + deallocate(src);
4067 +}
4068 +/* }}} */
4069 +
4070 +#ifdef ZEND_ENGINE_2
4071 +/* {{{ my_free_property_info */
4072 +static void my_free_property_info(zend_property_info* src, apc_free_t deallocate)
4073 +{
4074 + my_destroy_property_info(src, deallocate);
4075 + deallocate(src);
4076 +}
4077 +/* }}} */
4078 +
4079 +/* {{{ my_free_arg_info_array */
4080 +static void my_free_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
4081 +{
4082 + my_destroy_arg_info_array(src, num_args, deallocate);
4083 + deallocate(src);
4084 +}
4085 +/* }}} */
4086 +
4087 +/* {{{ my_free_arg_info */
4088 +static void my_free_arg_info(zend_arg_info* src, apc_free_t deallocate)
4089 +{
4090 + my_destroy_arg_info(src, deallocate);
4091 + deallocate(src);
4092 +}
4093 +/* }}} */
4094 +#endif
4095 +
4096 +/* {{{ my_free_function */
4097 +static void my_free_function(zend_function* src, apc_free_t deallocate)
4098 +{
4099 + my_destroy_function(src, deallocate);
4100 + deallocate(src);
4101 +}
4102 +/* }}} */
4103 +
4104 +/* {{{ my_free_hashtable */
4105 +static void my_free_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
4106 +{
4107 + my_destroy_hashtable(src, free_fn, deallocate);
4108 + deallocate(src);
4109 +}
4110 +/* }}} */
4111 +
4112 +/* {{{ apc_free_op_array */
4113 +void apc_free_op_array(zend_op_array* src, apc_free_t deallocate)
4114 +{
4115 + if (src != NULL) {
4116 + my_destroy_op_array(src, deallocate);
4117 + deallocate(src);
4118 + }
4119 +}
4120 +/* }}} */
4121 +
4122 +/* {{{ apc_free_functions */
4123 +void apc_free_functions(apc_function_t* src, apc_free_t deallocate)
4124 +{
4125 + int i;
4126 +
4127 + if (src != NULL) {
4128 + for (i = 0; src[i].function != NULL; i++) {
4129 + deallocate(src[i].name);
4130 + my_destroy_function(src[i].function, deallocate);
4131 + deallocate(src[i].function);
4132 + }
4133 + deallocate(src);
4134 + }
4135 +}
4136 +/* }}} */
4137 +
4138 +/* {{{ apc_free_classes */
4139 +void apc_free_classes(apc_class_t* src, apc_free_t deallocate)
4140 +{
4141 + int i;
4142 +
4143 + if (src != NULL) {
4144 + for (i = 0; src[i].class_entry != NULL; i++) {
4145 + deallocate(src[i].name);
4146 + deallocate(src[i].parent_name);
4147 + my_destroy_class_entry(src[i].class_entry, deallocate);
4148 + deallocate(src[i].class_entry);
4149 + }
4150 + deallocate(src);
4151 + }
4152 +}
4153 +/* }}} */
4154 +
4155 +/* {{{ apc_free_zval */
4156 +void apc_free_zval(zval* src, apc_free_t deallocate)
4157 +{
4158 + if (src != NULL) {
4159 + if(my_destroy_zval(src, deallocate) == SUCCESS) {
4160 + deallocate(src);
4161 + }
4162 + }
4163 +}
4164 +/* }}} */
4165 +
4166 +
4167 +/* Used only by my_prepare_op_array_for_execution */
4168 +#define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION() \
4169 + /* The fetch is only required if auto_globals_jit=1 */ \
4170 + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && \
4171 + zo->op1.op_type == IS_CONST && \
4172 + zo->op1.u.constant.type == IS_STRING && \
4173 + zo->op1.u.constant.value.str.val[0] == '_') { \
4174 + \
4175 + znode* varname = &zo->op1; \
4176 + (void)zend_is_auto_global(varname->u.constant.value.str.val, \
4177 + varname->u.constant.value.str.len \
4178 + TSRMLS_CC); \
4179 + } \
4180 +
4181 +/* {{{ my_prepare_op_array_for_execution */
4182 +static int my_prepare_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC)
4183 +{
4184 + /* combine my_fetch_global_vars and my_copy_data_exceptions.
4185 + * - Pre-fetch superglobals which would've been pre-fetched in parse phase.
4186 + * - If the opcode stream contain mutable data, ensure a copy.
4187 + * - Fixup array jumps in the same loop.
4188 + */
4189 + int i=src->last;
4190 + zend_op *zo;
4191 + zend_op *dzo;
4192 +#ifdef ZEND_ENGINE_2
4193 + apc_opflags_t * flags = APCG(reserved_offset) != -1 ?
4194 + (apc_opflags_t*) & (src->reserved[APCG(reserved_offset)]) : NULL;
4195 + int needcopy = flags ? flags->deep_copy : 1;
4196 + /* auto_globals_jit was not in php4 */
4197 + int do_prepare_fetch_global = PG(auto_globals_jit) && (flags == NULL || flags->unknown_global);
4198 +
4199 +#define FETCH_AUTOGLOBAL(member) do { \
4200 + if(flags && flags->member == 1) { \
4201 + zend_is_auto_global(#member,\
4202 + (sizeof(#member) - 1)\
4203 + TSRMLS_CC);\
4204 + } \
4205 +}while(0);
4206 +
4207 + FETCH_AUTOGLOBAL(_GET);
4208 + FETCH_AUTOGLOBAL(_POST);
4209 + FETCH_AUTOGLOBAL(_COOKIE);
4210 + FETCH_AUTOGLOBAL(_SERVER);
4211 + FETCH_AUTOGLOBAL(_ENV);
4212 + FETCH_AUTOGLOBAL(_FILES);
4213 + FETCH_AUTOGLOBAL(_REQUEST);
4214 +
4215 +#else
4216 + int needcopy = 0;
4217 + int do_prepare_fetch_global = 0;
4218 + int j = 0;
4219 +
4220 + for(j = 0; j < src->last; j++) {
4221 + zo = &src->opcodes[j];
4222 +
4223 + if( ((zo->op1.op_type == IS_CONST &&
4224 + zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||
4225 + ((zo->op2.op_type == IS_CONST &&
4226 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
4227 + needcopy = 1;
4228 + }
4229 + }
4230 +#endif
4231 +
4232 + if(needcopy) {
4233 +
4234 + dst->opcodes = (zend_op*) apc_xmemcpy(src->opcodes,
4235 + sizeof(zend_op) * src->last,
4236 + apc_php_malloc);
4237 + zo = src->opcodes;
4238 + dzo = dst->opcodes;
4239 + while(i > 0) {
4240 +
4241 + if( ((zo->op1.op_type == IS_CONST &&
4242 + zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||
4243 + ((zo->op2.op_type == IS_CONST &&
4244 + zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
4245 +
4246 + if(!(my_copy_zend_op(dzo, zo, apc_php_malloc, apc_php_free))) {
4247 + assert(0); /* emalloc failed or a bad constant array */
4248 + }
4249 + }
4250 +
4251 +#ifdef ZEND_ENGINE_2
4252 + switch(zo->opcode) {
4253 + case ZEND_JMP:
4254 + dzo->op1.u.jmp_addr = dst->opcodes +
4255 + (zo->op1.u.jmp_addr - src->opcodes);
4256 + break;
4257 + case ZEND_JMPZ:
4258 + case ZEND_JMPNZ:
4259 + case ZEND_JMPZ_EX:
4260 + case ZEND_JMPNZ_EX:
4261 + dzo->op2.u.jmp_addr = dst->opcodes +
4262 + (zo->op2.u.jmp_addr - src->opcodes);
4263 + break;
4264 + case ZEND_FETCH_R:
4265 + case ZEND_FETCH_W:
4266 + case ZEND_FETCH_IS:
4267 + case ZEND_FETCH_FUNC_ARG:
4268 + if(do_prepare_fetch_global)
4269 + {
4270 + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
4271 + }
4272 + break;
4273 + default:
4274 + break;
4275 + }
4276 +#endif
4277 + i--;
4278 + zo++;
4279 + dzo++;
4280 + }
4281 +#ifdef ZEND_ENGINE_2
4282 + } else { /* !needcopy */
4283 + /* The fetch is only required if auto_globals_jit=1 */
4284 + if(do_prepare_fetch_global)
4285 + {
4286 + zo = src->opcodes;
4287 + while(i > 0) {
4288 +
4289 + if(zo->opcode == ZEND_FETCH_R ||
4290 + zo->opcode == ZEND_FETCH_W ||
4291 + zo->opcode == ZEND_FETCH_IS ||
4292 + zo->opcode == ZEND_FETCH_FUNC_ARG
4293 + ) {
4294 + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
4295 + }
4296 +
4297 + i--;
4298 + zo++;
4299 + }
4300 + }
4301 +#endif
4302 + }
4303 + return 1;
4304 +}
4305 +/* }}} */
4306 +
4307 +/* {{{ apc_copy_op_array_for_execution */
4308 +zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC)
4309 +{
4310 + if(dst == NULL) {
4311 + dst = (zend_op_array*) emalloc(sizeof(src[0]));
4312 + }
4313 + memcpy(dst, src, sizeof(src[0]));
4314 + dst->static_variables = my_copy_static_variables(src, apc_php_malloc, apc_php_free);
4315 +
4316 + dst->refcount = apc_xmemcpy(src->refcount,
4317 + sizeof(src->refcount[0]),
4318 + apc_php_malloc);
4319 +
4320 + my_prepare_op_array_for_execution(dst,src TSRMLS_CC);
4321 +
4322 + return dst;
4323 +}
4324 +/* }}} */
4325 +
4326 +/* {{{ apc_copy_function_for_execution */
4327 +zend_function* apc_copy_function_for_execution(zend_function* src)
4328 +{
4329 + zend_function* dst;
4330 + TSRMLS_FETCH();
4331 +
4332 + dst = (zend_function*) emalloc(sizeof(src[0]));
4333 + memcpy(dst, src, sizeof(src[0]));
4334 + apc_copy_op_array_for_execution(&(dst->op_array), &(src->op_array) TSRMLS_CC);
4335 + return dst;
4336 +}
4337 +/* }}} */
4338 +
4339 +/* {{{ apc_copy_function_for_execution_ex */
4340 +zend_function* apc_copy_function_for_execution_ex(void *dummy, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
4341 +{
4342 + if(src->type==ZEND_INTERNAL_FUNCTION || src->type==ZEND_OVERLOADED_FUNCTION) return src;
4343 + return apc_copy_function_for_execution(src);
4344 +}
4345 +/* }}} */
4346 +
4347 +/* {{{ apc_copy_class_entry_for_execution */
4348 +zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, int is_derived)
4349 +{
4350 + zend_class_entry* dst = (zend_class_entry*) emalloc(sizeof(src[0]));
4351 + memcpy(dst, src, sizeof(src[0]));
4352 +
4353 +#ifdef ZEND_ENGINE_2
4354 + if(src->num_interfaces)
4355 + {
4356 + /* These are slots to be populated later by ADD_INTERFACE insns */
4357 + dst->interfaces = apc_php_malloc(
4358 + sizeof(zend_class_entry*) * src->num_interfaces);
4359 + memset(dst->interfaces, 0,
4360 + sizeof(zend_class_entry*) * src->num_interfaces);
4361 + }
4362 + else
4363 + {
4364 + /* assert(dst->interfaces == NULL); */
4365 + }
4366 +#endif
4367 +
4368 +#ifndef ZEND_ENGINE_2
4369 + dst->refcount = apc_xmemcpy(src->refcount,
4370 + sizeof(src->refcount[0]),
4371 + apc_php_malloc);
4372 +#endif
4373 +
4374 + /* Deep-copy the class properties, because they will be modified */
4375 +
4376 + my_copy_hashtable(&dst->default_properties,
4377 + &src->default_properties,
4378 + (ht_copy_fun_t) my_copy_zval_ptr,
4379 + (ht_free_fun_t) my_free_zval_ptr,
4380 + 1,
4381 + apc_php_malloc, apc_php_free);
4382 +
4383 + /* For derived classes, we must also copy the function hashtable (although
4384 + * we can merely bitwise copy the functions it contains) */
4385 +
4386 + my_copy_hashtable(&dst->function_table,
4387 + &src->function_table,
4388 + (ht_copy_fun_t) apc_copy_function_for_execution_ex,
4389 + NULL,
4390 + 0,
4391 + apc_php_malloc, apc_php_free);
4392 +#ifdef ZEND_ENGINE_2
4393 + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function_for_execution, src, dst);
4394 +
4395 + /* zend_do_inheritance merges properties_info.
4396 + * Need only shallow copying as it doesn't hold the pointers.
4397 + */
4398 + my_copy_hashtable(&dst->properties_info,
4399 + &src->properties_info,
4400 + (ht_copy_fun_t) my_copy_property_info_for_execution,
4401 + NULL,
4402 + 0,
4403 + apc_php_malloc, apc_php_free);
4404 +
4405 +#ifdef ZEND_ENGINE_2_2
4406 + /* php5.2 introduced a scope attribute for property info */
4407 + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
4408 +#endif
4409 +
4410 + /* if inheritance results in a hash_del, it might result in
4411 + * a pefree() of the pointers here. Deep copying required.
4412 + */
4413 +
4414 + my_copy_hashtable(&dst->constants_table,
4415 + &src->constants_table,
4416 + (ht_copy_fun_t) my_copy_zval_ptr,
4417 + NULL,
4418 + 1,
4419 + apc_php_malloc, apc_php_free);
4420 +
4421 + my_copy_hashtable(&dst->default_static_members,
4422 + &src->default_static_members,
4423 + (ht_copy_fun_t) my_copy_zval_ptr,
4424 + (ht_free_fun_t) my_free_zval_ptr,
4425 + 1,
4426 + apc_php_malloc, apc_php_free);
4427 +
4428 + if(src->static_members != &(src->default_static_members))
4429 + {
4430 + dst->static_members = my_copy_hashtable(NULL,
4431 + src->static_members,
4432 + (ht_copy_fun_t) my_copy_zval_ptr,
4433 + (ht_free_fun_t) my_free_zval_ptr,
4434 + 1,
4435 + apc_php_malloc, apc_php_free);
4436 + }
4437 + else
4438 + {
4439 + dst->static_members = &(dst->default_static_members);
4440 + }
4441 +
4442 +#endif
4443 +
4444 + return dst;
4445 +}
4446 +/* }}} */
4447 +
4448 +/* {{{ apc_free_class_entry_after_execution */
4449 +void apc_free_class_entry_after_execution(zend_class_entry* src)
4450 +{
4451 +#ifdef ZEND_ENGINE_2
4452 + if(src->num_interfaces > 0 && src->interfaces) {
4453 + apc_php_free(src->interfaces);
4454 + src->interfaces = NULL;
4455 + src->num_interfaces = 0;
4456 + }
4457 + /* my_destroy_hashtable() does not play nice with refcounts */
4458 +
4459 + zend_hash_clean(&src->default_static_members);
4460 + if(src->static_members != &(src->default_static_members))
4461 + {
4462 + zend_hash_destroy(src->static_members);
4463 + apc_php_free(src->static_members);
4464 + src->static_members = NULL;
4465 + }
4466 + else
4467 + {
4468 + src->static_members = NULL;
4469 + }
4470 + zend_hash_clean(&src->default_properties);
4471 + zend_hash_clean(&src->constants_table);
4472 +#endif
4473 +
4474 + /* TODO: more cleanup */
4475 +}
4476 +/* }}} */
4477 +
4478 +#ifdef ZEND_ENGINE_2
4479 +
4480 +/* {{{ my_fixup_function */
4481 +static void my_fixup_function(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
4482 +{
4483 + zend_function* zf = p->pData;
4484 +
4485 + #define SET_IF_SAME_NAME(member) \
4486 + do { \
4487 + if(src->member && !strcmp(zf->common.function_name, src->member->common.function_name)) { \
4488 + dst->member = zf; \
4489 + } \
4490 + } \
4491 + while(0)
4492 +
4493 + if(zf->common.scope == src)
4494 + {
4495 +
4496 + /* Fixing up the default functions for objects here since
4497 + * we need to compare with the newly allocated functions
4498 + *
4499 + * caveat: a sub-class method can have the same name as the
4500 + * parent's constructor and create problems.
4501 + */
4502 +
4503 + if(zf->common.fn_flags & ZEND_ACC_CTOR) dst->constructor = zf;
4504 + else if(zf->common.fn_flags & ZEND_ACC_DTOR) dst->destructor = zf;
4505 + else if(zf->common.fn_flags & ZEND_ACC_CLONE) dst->clone = zf;
4506 + else
4507 + {
4508 + SET_IF_SAME_NAME(__get);
4509 + SET_IF_SAME_NAME(__set);
4510 + SET_IF_SAME_NAME(__unset);
4511 + SET_IF_SAME_NAME(__isset);
4512 + SET_IF_SAME_NAME(__call);
4513 +#ifdef ZEND_ENGINE_2_2
4514 + SET_IF_SAME_NAME(__tostring);
4515 +#endif
4516 + }
4517 + zf->common.scope = dst;
4518 + }
4519 + else
4520 + {
4521 + /* no other function should reach here */
4522 + assert(0);
4523 + }
4524 +
4525 + #undef SET_IF_SAME_NAME
4526 +}
4527 +/* }}} */
4528 +
4529 +#ifdef ZEND_ENGINE_2_2
4530 +/* {{{ my_fixup_property_info */
4531 +static void my_fixup_property_info(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
4532 +{
4533 + zend_property_info* property_info = (zend_property_info*)p->pData;
4534 +
4535 + if(property_info->ce == src)
4536 + {
4537 + property_info->ce = dst;
4538 + }
4539 + else
4540 + {
4541 + assert(0); /* should never happen */
4542 + }
4543 +}
4544 +/* }}} */
4545 +#endif
4546 +
4547 +/* {{{ my_fixup_hashtable */
4548 +static void my_fixup_hashtable(HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst)
4549 +{
4550 + Bucket *p;
4551 +
4552 + uint i;
4553 +
4554 + for (i = 0; i < ht->nTableSize; i++) {
4555 + if(!ht->arBuckets) break;
4556 + p = ht->arBuckets[i];
4557 + while (p != NULL) {
4558 + fixup(p, src, dst);
4559 + p = p->pNext;
4560 + }
4561 + }
4562 +}
4563 +/* }}} */
4564 +
4565 +#endif
4566 +
4567 +/* {{{ my_check_copy_function */
4568 +static int my_check_copy_function(Bucket* p, va_list args)
4569 +{
4570 + zend_class_entry* src = va_arg(args, zend_class_entry*);
4571 + zend_function* zf = (zend_function*)p->pData;
4572 +#ifndef ZEND_ENGINE_2
4573 + zend_class_entry* parent = src->parent;
4574 + zend_function* parent_fn = NULL;
4575 +#endif
4576 +
4577 +#ifdef ZEND_ENGINE_2
4578 + return (zf->common.scope == src);
4579 +#else
4580 + if (parent &&
4581 + zend_hash_quick_find(&parent->function_table, p->arKey,
4582 + p->nKeyLength, p->h, (void **) &parent_fn)==SUCCESS) {
4583 +
4584 + if((parent_fn && zf) &&
4585 + (parent_fn->op_array.refcount == zf->op_array.refcount))
4586 + {
4587 + return 0;
4588 + }
4589 + }
4590 + return 1;
4591 +#endif
4592 +}
4593 +/* }}} */
4594 +
4595 +/* {{{ my_check_copy_default_property */
4596 +static int my_check_copy_default_property(Bucket* p, va_list args)
4597 +{
4598 + zend_class_entry* src = va_arg(args, zend_class_entry*);
4599 + zend_class_entry* parent = src->parent;
4600 + zval ** child_prop = (zval**)p->pData;
4601 + zval ** parent_prop = NULL;
4602 +
4603 + if (parent &&
4604 + zend_hash_quick_find(&parent->default_properties, p->arKey,
4605 + p->nKeyLength, p->h, (void **) &parent_prop)==SUCCESS) {
4606 +
4607 + if((parent_prop && child_prop) && (*parent_prop) == (*child_prop))