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