New PHP5 APC - version 3.0.19, using PHP5 5.2.0-8+etch11,
[php5-apc.git] / apc_zend.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
16   +----------------------------------------------------------------------+
17
18    This software was contributed to PHP by Community Connect Inc. in 2002
19    and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
20    Future revisions and derivatives of this source code must acknowledge
21    Community Connect Inc. as the original contributor of this module by
22    leaving this note intact in the source code.
23
24    All other licensing and usage conditions are those of the PHP Group.
25
26  */
27
28 /* $Id: apc_zend.c,v 3.14.2.1 2008/05/11 18:57:00 rasmus Exp $ */
29
30 #include "apc_zend.h"
31 #include "apc_globals.h"
32
33 void* apc_php_malloc(size_t n)
34 {
35     return emalloc(n);
36 }
37
38 void apc_php_free(void* p)
39 {
40     efree(p);
41 }
42
43 #ifndef ZEND_VM_KIND_CALL /* Not currently defined by any ZE version */
44 # define ZEND_VM_KIND_CALL      1
45 #endif
46
47 #ifndef ZEND_VM_KIND /* Indicates PHP < 5.1 */
48 # define ZEND_VM_KIND   ZEND_VM_KIND_CALL
49 #endif
50
51 #if defined(ZEND_ENGINE_2) && (ZEND_VM_KIND == ZEND_VM_KIND_CALL)
52 # define APC_OPCODE_OVERRIDE
53 #endif
54
55 #ifdef APC_OPCODE_OVERRIDE
56
57 #ifdef ZEND_ENGINE_2_1
58 /* Taken from Zend/zend_vm_execute.h */
59 #define _CONST_CODE  0
60 #define _TMP_CODE    1
61 #define _VAR_CODE    2
62 #define _UNUSED_CODE 3
63 #define _CV_CODE     4
64 static inline int _apc_opcode_handler_decode(zend_op *opline)
65 {
66         static const int apc_vm_decode[] = {
67                 _UNUSED_CODE, /* 0              */
68                 _CONST_CODE,  /* 1 = IS_CONST   */
69                 _TMP_CODE,    /* 2 = IS_TMP_VAR */
70                 _UNUSED_CODE, /* 3              */
71                 _VAR_CODE,    /* 4 = IS_VAR     */
72                 _UNUSED_CODE, /* 5              */
73                 _UNUSED_CODE, /* 6              */
74                 _UNUSED_CODE, /* 7              */
75                 _UNUSED_CODE, /* 8 = IS_UNUSED  */
76                 _UNUSED_CODE, /* 9              */
77                 _UNUSED_CODE, /* 10             */
78                 _UNUSED_CODE, /* 11             */
79                 _UNUSED_CODE, /* 12             */
80                 _UNUSED_CODE, /* 13             */
81                 _UNUSED_CODE, /* 14             */
82                 _UNUSED_CODE, /* 15             */
83                 _CV_CODE      /* 16 = IS_CV     */
84         };
85         return (opline->opcode * 25) + (apc_vm_decode[opline->op1.op_type] * 5) + apc_vm_decode[opline->op2.op_type];
86 }
87
88 # define APC_ZEND_OPLINE                                        zend_op *opline = execute_data->opline;
89 # define APC_OPCODE_HANDLER_DECODE(opline)      _apc_opcode_handler_decode(opline)
90 # if PHP_MAJOR_VERSION >= 6
91 #  define APC_OPCODE_HANDLER_COUNT                      ((25 * 152) + 1)
92 # else
93 #  define APC_OPCODE_HANDLER_COUNT                      ((25 * 151) + 1)
94 # endif
95 # define APC_REPLACE_OPCODE(opname)         { int i; for(i = 0; i < 25; i++) if (zend_opcode_handlers[(opname*25) + i]) zend_opcode_handlers[(opname*25) + i] = apc_op_##opname; }
96
97 #else /* ZE2.0 */
98 # define APC_ZEND_ONLINE
99 # define APC_OPCODE_HANDLER_DECODE(opline)      (opline->opcode)
100 # define APC_OPCODE_HANDLER_COUNT                       512
101 # define APC_REPLACE_OPCODE(opname)                     zend_opcode_handlers[opname] = apc_op_##opname;
102 #endif
103
104 static opcode_handler_t *apc_original_opcode_handlers;
105 static opcode_handler_t apc_opcode_handlers[APC_OPCODE_HANDLER_COUNT];
106
107 #define APC_EX_T(offset)                                        (*(temp_variable *)((char*)execute_data->Ts + offset))
108
109 static zval *apc_get_zval_ptr(znode *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC)
110 {
111         *freeval = NULL;
112
113         switch (node->op_type) {
114                 case IS_CONST:
115                         return &(node->u.constant);
116                 case IS_VAR:
117                         return APC_EX_T(node->u.var).var.ptr;
118                 case IS_TMP_VAR:
119                         return (*freeval = &APC_EX_T(node->u.var).tmp_var);
120 #ifdef ZEND_ENGINE_2_1
121                 case IS_CV:
122                 {
123                         zval ***ret = &execute_data->CVs[node->u.var];
124
125                         if (!*ret) {
126                                 zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var];
127
128                                 if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) {
129                                         apc_nprint("Undefined variable: %s", cv->name);
130                                         return &EG(uninitialized_zval);
131                                 }
132                         }
133                         return **ret;
134                 }
135 #endif
136                 case IS_UNUSED:
137                 default:
138                         return NULL;
139         }
140 }
141
142 static int apc_op_ZEND_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS)
143 {
144         APC_ZEND_OPLINE
145         zval *freeop1 = NULL;
146         zval *inc_filename = NULL, tmp_inc_filename;
147         char realpath[MAXPATHLEN];
148         php_stream_wrapper *wrapper;
149         char *path_for_open;
150         int ret = 0;
151         #ifdef ZEND_ENGINE_2
152         apc_opflags_t* flags = NULL;
153         #endif
154
155         if (Z_LVAL(opline->op2.u.constant) != ZEND_INCLUDE_ONCE &&
156                 Z_LVAL(opline->op2.u.constant) != ZEND_REQUIRE_ONCE) {
157                 return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
158         }
159
160         inc_filename = apc_get_zval_ptr(&opline->op1, &freeop1, execute_data TSRMLS_CC);
161         if (Z_TYPE_P(inc_filename) != IS_STRING) {
162                 tmp_inc_filename = *inc_filename;
163                 zval_copy_ctor(&tmp_inc_filename);
164                 convert_to_string(&tmp_inc_filename);
165                 inc_filename = &tmp_inc_filename;
166         }
167
168         wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(inc_filename), &path_for_open, 0 TSRMLS_CC);
169         if (wrapper != &php_plain_files_wrapper ||
170                 !IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) ||
171                 !expand_filepath(path_for_open, realpath TSRMLS_CC)) {
172                 /* Fallback to original handler */
173                 if (inc_filename == &tmp_inc_filename) {
174                         zval_dtor(&tmp_inc_filename);
175                 }
176                 return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
177         }
178
179         if (zend_hash_exists(&EG(included_files), realpath, strlen(realpath) + 1)) {
180                 if (!(opline->result.u.EA.type & EXT_TYPE_UNUSED)) {
181                         ALLOC_INIT_ZVAL(APC_EX_T(opline->result.u.var).var.ptr);
182                         ZVAL_TRUE(APC_EX_T(opline->result.u.var).var.ptr);
183                 }
184                 if (inc_filename == &tmp_inc_filename) {
185                         zval_dtor(&tmp_inc_filename);
186                 }
187                 if (freeop1) {
188                         zval_dtor(freeop1);
189                 }
190                 execute_data->opline++;
191                 return 0;
192         }
193
194         if (inc_filename == &tmp_inc_filename) {
195                 zval_dtor(&tmp_inc_filename);
196         }
197
198         if(APCG(reserved_offset) != -1) {
199                 /* Insanity alert: look into apc_compile.c for why a void** is cast to a apc_opflags_t* */
200                 flags = (apc_opflags_t*) & (execute_data->op_array->reserved[APCG(reserved_offset)]);
201         }
202
203 #ifdef ZEND_ENGINE_2
204         if(flags && flags->deep_copy == 1) {
205                 /* Since the op array is a local copy, we can cheat our way through the file inclusion by temporarily 
206                  * changing the op to a plain require/include, calling its handler and finally restoring the opcode.
207                  */
208                 Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE;
209                 ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
210                 Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE;
211 #else 
212         if(0) {
213                 /* do nothing, have nothing, be nothing */
214 #endif
215         } else {
216                 ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
217         }
218
219         return ret;
220 }
221
222 void apc_zend_init(TSRMLS_D)
223 {
224     zend_extension dummy_ext;
225 #ifdef ZEND_ENGINE_2
226     APCG(reserved_offset) = zend_get_resource_handle(&dummy_ext); 
227     assert(APCG(reserved_offset) == dummy_ext.resource_number);
228     assert(APCG(reserved_offset) != -1);
229     assert(sizeof(apc_opflags_t) <= sizeof(void*));
230 #endif
231         if (!APCG(include_once)) {
232                 /* If we're not overriding the INCLUDE_OR_EVAL handler, then just skip this malarkey */
233                 return;
234         }
235
236         memcpy(apc_opcode_handlers, zend_opcode_handlers, sizeof(apc_opcode_handlers));
237
238         /* 5.0 exposes zend_opcode_handlers differently than 5.1 and later */
239 #ifdef ZEND_ENGINE_2_1
240         apc_original_opcode_handlers = zend_opcode_handlers;
241         zend_opcode_handlers = apc_opcode_handlers;
242 #else
243         apc_original_opcode_handlers = apc_opcode_handlers;
244 #endif
245
246         APC_REPLACE_OPCODE(ZEND_INCLUDE_OR_EVAL);
247 }
248
249 void apc_zend_shutdown(TSRMLS_D)
250 {
251         if (!APCG(include_once)) {
252                 /* Nothing changed, nothing to restore */
253                 return;
254         }
255
256 #ifdef ZEND_ENGINE_2_1
257         zend_opcode_handlers = apc_original_opcode_handlers;
258 #else
259         memcpy(zend_opcode_handlers, apc_original_opcode_handlers, sizeof(apc_opcode_handlers));
260 #endif
261 }
262
263 #else /* Opcode Overrides unavailable */
264
265 void apc_zend_init(TSRMLS_D) { }
266 void apc_zend_shutdown(TSRMLS_D) { }
267
268 #endif /* APC_OPCODE_OVERRIDE */
269
270 /*
271  * Local variables:
272  * tab-width: 4
273  * c-basic-offset: 4
274  * End:
275  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
276  * vim<600: expandtab sw=4 ts=4 sts=4
277  */