1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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: Andi Gutmans <andi@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@zend.com> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include <time.h>
23
24 #include "php.h"
25 #include "ZendAccelerator.h"
26 #include "zend_API.h"
27 #include "zend_shared_alloc.h"
28 #include "zend_accelerator_blacklist.h"
29 #include "php_ini.h"
30 #include "SAPI.h"
31 #include "zend_virtual_cwd.h"
32 #include "ext/standard/info.h"
33 #include "ext/standard/php_filestat.h"
34
35 #define STRING_NOT_NULL(s) (NULL == (s)?"":s)
36 #define MIN_ACCEL_FILES 200
37 #define MAX_ACCEL_FILES 1000000
38 #define TOKENTOSTR(X) #X
39
40 static zif_handler orig_file_exists = NULL;
41 static zif_handler orig_is_file = NULL;
42 static zif_handler orig_is_readable = NULL;
43
44 ZEND_BEGIN_ARG_INFO(arginfo_opcache_none, 0)
45 ZEND_END_ARG_INFO()
46
47 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_get_status, 0, 0, 0)
48 ZEND_ARG_INFO(0, fetch_scripts)
49 ZEND_END_ARG_INFO()
50
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_compile_file, 0, 0, 1)
52 ZEND_ARG_INFO(0, file)
53 ZEND_END_ARG_INFO()
54
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_invalidate, 0, 0, 1)
56 ZEND_ARG_INFO(0, script)
57 ZEND_ARG_INFO(0, force)
58 ZEND_END_ARG_INFO()
59
60 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_is_script_cached, 0, 0, 1)
61 ZEND_ARG_INFO(0, script)
62 ZEND_END_ARG_INFO()
63
64 /* User functions */
65 static ZEND_FUNCTION(opcache_reset);
66 static ZEND_FUNCTION(opcache_invalidate);
67 static ZEND_FUNCTION(opcache_is_script_cached);
68
69 /* Private functions */
70 static ZEND_FUNCTION(opcache_get_status);
71 static ZEND_FUNCTION(opcache_compile_file);
72 static ZEND_FUNCTION(opcache_get_configuration);
73
74 static zend_function_entry accel_functions[] = {
75 /* User functions */
76 ZEND_FE(opcache_reset, arginfo_opcache_none)
77 ZEND_FE(opcache_invalidate, arginfo_opcache_invalidate)
78 ZEND_FE(opcache_compile_file, arginfo_opcache_compile_file)
79 ZEND_FE(opcache_is_script_cached, arginfo_opcache_is_script_cached)
80 /* Private functions */
81 ZEND_FE(opcache_get_configuration, arginfo_opcache_none)
82 ZEND_FE(opcache_get_status, arginfo_opcache_get_status)
83 ZEND_FE_END
84 };
85
validate_api_restriction(void)86 static int validate_api_restriction(void)
87 {
88 if (ZCG(accel_directives).restrict_api && *ZCG(accel_directives).restrict_api) {
89 size_t len = strlen(ZCG(accel_directives).restrict_api);
90
91 if (!SG(request_info).path_translated ||
92 strlen(SG(request_info).path_translated) < len ||
93 memcmp(SG(request_info).path_translated, ZCG(accel_directives).restrict_api, len) != 0) {
94 zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " API is restricted by \"restrict_api\" configuration directive");
95 return 0;
96 }
97 }
98 return 1;
99 }
100
ZEND_INI_MH(OnUpdateMemoryConsumption)101 static ZEND_INI_MH(OnUpdateMemoryConsumption)
102 {
103 zend_long *p;
104 zend_long memsize;
105 #ifndef ZTS
106 char *base = (char *) mh_arg2;
107 #else
108 char *base = (char *) ts_resource(*((int *) mh_arg2));
109 #endif
110
111 /* keep the compiler happy */
112 (void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
113
114 p = (zend_long *) (base + (size_t)mh_arg1);
115 memsize = atoi(ZSTR_VAL(new_value));
116 /* sanity check we must use at least 8 MB */
117 if (memsize < 8) {
118 const char *new_new_value = "8";
119 zend_ini_entry *ini_entry;
120
121 memsize = 8;
122 zend_accel_error(ACCEL_LOG_WARNING, "opcache.memory_consumption is set below the required 8MB.\n");
123 zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB configuration.\n");
124
125 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
126 "opcache.memory_consumption",
127 sizeof("opcache.memory_consumption")-1)) == NULL) {
128 return FAILURE;
129 }
130
131 ini_entry->value = zend_string_init(new_new_value, 1, 1);
132 }
133 if (UNEXPECTED(memsize > ZEND_ULONG_MAX / (1024 * 1024))) {
134 *p = ZEND_ULONG_MAX;
135 } else {
136 *p = memsize * (1024 * 1024);
137 }
138 return SUCCESS;
139 }
140
ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)141 static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
142 {
143 zend_long *p;
144 zend_long size;
145 #ifndef ZTS
146 char *base = (char *) mh_arg2;
147 #else
148 char *base = (char *) ts_resource(*((int *) mh_arg2));
149 #endif
150
151 /* keep the compiler happy */
152 (void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
153
154 p = (zend_long *) (base + (size_t)mh_arg1);
155 size = atoi(ZSTR_VAL(new_value));
156 /* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */
157
158 if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) {
159 const char *new_new_value;
160 zend_ini_entry *ini_entry;
161
162 if (size < MIN_ACCEL_FILES) {
163 size = MIN_ACCEL_FILES;
164 new_new_value = TOKENTOSTR(MIN_ACCEL_FILES);
165 zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES);
166 zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal configuration.\n");
167 }
168 if (size > MAX_ACCEL_FILES) {
169 size = MAX_ACCEL_FILES;
170 new_new_value = TOKENTOSTR(MAX_ACCEL_FILES);
171 zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES);
172 zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal configuration.\n");
173 }
174 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
175 "opcache.max_accelerated_files",
176 sizeof("opcache.max_accelerated_files")-1)) == NULL) {
177 return FAILURE;
178 }
179 ini_entry->value = zend_string_init(new_new_value, strlen(new_new_value), 1);
180 }
181 *p = size;
182 return SUCCESS;
183 }
184
ZEND_INI_MH(OnUpdateMaxWastedPercentage)185 static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
186 {
187 double *p;
188 zend_long percentage;
189 #ifndef ZTS
190 char *base = (char *) mh_arg2;
191 #else
192 char *base = (char *) ts_resource(*((int *) mh_arg2));
193 #endif
194
195 /* keep the compiler happy */
196 (void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
197
198 p = (double *) (base + (size_t)mh_arg1);
199 percentage = atoi(ZSTR_VAL(new_value));
200
201 if (percentage <= 0 || percentage > 50) {
202 const char *new_new_value = "5";
203 zend_ini_entry *ini_entry;
204
205 percentage = 5;
206 zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_wasted_percentage must be set between 1 and 50.\n");
207 zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%%.\n");
208 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
209 "opcache.max_wasted_percentage",
210 sizeof("opcache.max_wasted_percentage")-1)) == NULL) {
211 return FAILURE;
212 }
213 ini_entry->value = zend_string_init(new_new_value, strlen(new_new_value), 1);
214 }
215 *p = (double)percentage / 100.0;
216 return SUCCESS;
217 }
218
ZEND_INI_MH(OnEnable)219 static ZEND_INI_MH(OnEnable)
220 {
221 if (stage == ZEND_INI_STAGE_STARTUP ||
222 stage == ZEND_INI_STAGE_SHUTDOWN ||
223 stage == ZEND_INI_STAGE_DEACTIVATE) {
224 return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
225 } else {
226 /* It may be only temporary disabled */
227 zend_bool *p;
228 #ifndef ZTS
229 char *base = (char *) mh_arg2;
230 #else
231 char *base = (char *) ts_resource(*((int *) mh_arg2));
232 #endif
233
234 p = (zend_bool *) (base+(size_t) mh_arg1);
235 if ((ZSTR_LEN(new_value) == 2 && strcasecmp("on", ZSTR_VAL(new_value)) == 0) ||
236 (ZSTR_LEN(new_value) == 3 && strcasecmp("yes", ZSTR_VAL(new_value)) == 0) ||
237 (ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0) ||
238 atoi(ZSTR_VAL(new_value)) != 0) {
239 zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " can't be temporary enabled (it may be only disabled till the end of request)");
240 return FAILURE;
241 } else {
242 *p = 0;
243 ZCG(accelerator_enabled) = 0;
244 return SUCCESS;
245 }
246 }
247 }
248
249 #ifdef HAVE_OPCACHE_FILE_CACHE
ZEND_INI_MH(OnUpdateFileCache)250 static ZEND_INI_MH(OnUpdateFileCache)
251 {
252 if (new_value) {
253 if (!ZSTR_LEN(new_value)) {
254 new_value = NULL;
255 } else {
256 zend_stat_t buf;
257
258 if (!IS_ABSOLUTE_PATH(ZSTR_VAL(new_value), ZSTR_LEN(new_value)) ||
259 zend_stat(ZSTR_VAL(new_value), &buf) != 0 ||
260 !S_ISDIR(buf.st_mode) ||
261 #ifndef ZEND_WIN32
262 access(ZSTR_VAL(new_value), R_OK | W_OK | X_OK) != 0) {
263 #else
264 _access(ZSTR_VAL(new_value), 06) != 0) {
265 #endif
266 zend_accel_error(ACCEL_LOG_WARNING, "opcache.file_cache must be a full path of accessable directory.\n");
267 new_value = NULL;
268 }
269 }
270 }
271 OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
272 return SUCCESS;
273 }
274 #endif
275
276 ZEND_INI_BEGIN()
277 STD_PHP_INI_BOOLEAN("opcache.enable" , "1", PHP_INI_ALL, OnEnable, enabled , zend_accel_globals, accel_globals)
278 STD_PHP_INI_BOOLEAN("opcache.use_cwd" , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd , zend_accel_globals, accel_globals)
279 STD_PHP_INI_BOOLEAN("opcache.validate_timestamps", "1", PHP_INI_ALL , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals)
280 STD_PHP_INI_BOOLEAN("opcache.validate_permission", "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_permission, zend_accel_globals, accel_globals)
281 #ifndef ZEND_WIN32
282 STD_PHP_INI_BOOLEAN("opcache.validate_root" , "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_root , zend_accel_globals, accel_globals)
283 #endif
284 STD_PHP_INI_BOOLEAN("opcache.inherited_hack" , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack , zend_accel_globals, accel_globals)
285 STD_PHP_INI_BOOLEAN("opcache.dups_fix" , "0", PHP_INI_ALL , OnUpdateBool, accel_directives.ignore_dups , zend_accel_globals, accel_globals)
286 STD_PHP_INI_BOOLEAN("opcache.revalidate_path" , "0", PHP_INI_ALL , OnUpdateBool, accel_directives.revalidate_path , zend_accel_globals, accel_globals)
287
288 STD_PHP_INI_ENTRY("opcache.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals)
289 STD_PHP_INI_ENTRY("opcache.memory_consumption" , "128" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals)
290 STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "8" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
291 STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "10000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals)
292 STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals)
293 STD_PHP_INI_ENTRY("opcache.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals)
294 STD_PHP_INI_ENTRY("opcache.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.force_restart_timeout, zend_accel_globals, accel_globals)
295 STD_PHP_INI_ENTRY("opcache.revalidate_freq" , "2" , PHP_INI_ALL , OnUpdateLong, accel_directives.revalidate_freq, zend_accel_globals, accel_globals)
296 STD_PHP_INI_ENTRY("opcache.file_update_protection", "2" , PHP_INI_ALL , OnUpdateLong, accel_directives.file_update_protection, zend_accel_globals, accel_globals)
297 STD_PHP_INI_ENTRY("opcache.preferred_memory_model", "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.memory_model, zend_accel_globals, accel_globals)
298 STD_PHP_INI_ENTRY("opcache.blacklist_filename" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.user_blacklist_filename, zend_accel_globals, accel_globals)
299 STD_PHP_INI_ENTRY("opcache.max_file_size" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.max_file_size, zend_accel_globals, accel_globals)
300
301 STD_PHP_INI_ENTRY("opcache.protect_memory" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.protect_memory, zend_accel_globals, accel_globals)
302 STD_PHP_INI_ENTRY("opcache.save_comments" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.save_comments, zend_accel_globals, accel_globals)
303
304 STD_PHP_INI_ENTRY("opcache.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals)
305 STD_PHP_INI_ENTRY("opcache.opt_debug_level" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.opt_debug_level, zend_accel_globals, accel_globals)
306 STD_PHP_INI_BOOLEAN("opcache.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals)
307 STD_PHP_INI_BOOLEAN("opcache.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals)
308 STD_PHP_INI_ENTRY("opcache.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals)
309 STD_PHP_INI_ENTRY("opcache.restrict_api" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.restrict_api, zend_accel_globals, accel_globals)
310
311 #ifndef ZEND_WIN32
312 STD_PHP_INI_ENTRY("opcache.lockfile_path" , "/tmp" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.lockfile_path, zend_accel_globals, accel_globals)
313 #else
314 STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals)
315 #endif
316
317 #ifdef HAVE_OPCACHE_FILE_CACHE
318 STD_PHP_INI_ENTRY("opcache.file_cache" , NULL , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache, zend_accel_globals, accel_globals)
319 STD_PHP_INI_ENTRY("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals)
320 STD_PHP_INI_ENTRY("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals)
321 #endif
322 #if ENABLE_FILE_CACHE_FALLBACK
323 STD_PHP_INI_ENTRY("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals)
324 #endif
325 #ifdef HAVE_HUGE_CODE_PAGES
326 STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals)
327 #endif
328 ZEND_INI_END()
329
330 static int filename_is_in_cache(zend_string *filename)
331 {
332 char *key;
333 int key_length;
334
335 key = accel_make_persistent_key(ZSTR_VAL(filename), ZSTR_LEN(filename), &key_length);
336 if (key != NULL) {
337 zend_persistent_script *persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
338 if (persistent_script && !persistent_script->corrupted) {
339 zend_file_handle handle = {{0}, NULL, NULL, 0, 0};
340
341 handle.filename = ZSTR_VAL(filename);
342 handle.type = ZEND_HANDLE_FILENAME;
343
344 if (ZCG(accel_directives).validate_timestamps) {
345 return validate_timestamp_and_record_ex(persistent_script, &handle) == SUCCESS;
346 }
347
348 return 1;
349 }
350 }
351
352 return 0;
353 }
354
355 static int accel_file_in_cache(INTERNAL_FUNCTION_PARAMETERS)
356 {
357 zval zfilename;
358
359 if (ZEND_NUM_ARGS() != 1 ||
360 zend_get_parameters_array_ex(1, &zfilename) == FAILURE ||
361 Z_TYPE(zfilename) != IS_STRING ||
362 Z_STRLEN(zfilename) == 0) {
363 return 0;
364 }
365 return filename_is_in_cache(Z_STR(zfilename));
366 }
367
368 static ZEND_NAMED_FUNCTION(accel_file_exists)
369 {
370 if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
371 RETURN_TRUE;
372 } else {
373 orig_file_exists(INTERNAL_FUNCTION_PARAM_PASSTHRU);
374 }
375 }
376
377 static ZEND_NAMED_FUNCTION(accel_is_file)
378 {
379 if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
380 RETURN_TRUE;
381 } else {
382 orig_is_file(INTERNAL_FUNCTION_PARAM_PASSTHRU);
383 }
384 }
385
386 static ZEND_NAMED_FUNCTION(accel_is_readable)
387 {
388 if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
389 RETURN_TRUE;
390 } else {
391 orig_is_readable(INTERNAL_FUNCTION_PARAM_PASSTHRU);
392 }
393 }
394
395 static ZEND_MINIT_FUNCTION(zend_accelerator)
396 {
397 (void)type; /* keep the compiler happy */
398
399 REGISTER_INI_ENTRIES();
400
401 return SUCCESS;
402 }
403
404 void zend_accel_override_file_functions(void)
405 {
406 zend_function *old_function;
407 if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) {
408 #ifdef HAVE_OPCACHE_FILE_CACHE
409 if (file_cache_only) {
410 zend_accel_error(ACCEL_LOG_WARNING, "file_override_enabled has no effect when file_cache_only is set");
411 return;
412 }
413 #endif
414 /* override file_exists */
415 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "file_exists", sizeof("file_exists")-1)) != NULL) {
416 orig_file_exists = old_function->internal_function.handler;
417 old_function->internal_function.handler = accel_file_exists;
418 }
419 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "is_file", sizeof("is_file")-1)) != NULL) {
420 orig_is_file = old_function->internal_function.handler;
421 old_function->internal_function.handler = accel_is_file;
422 }
423 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "is_readable", sizeof("is_readable")-1)) != NULL) {
424 orig_is_readable = old_function->internal_function.handler;
425 old_function->internal_function.handler = accel_is_readable;
426 }
427 }
428 }
429
430 static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)
431 {
432 (void)type; /* keep the compiler happy */
433
434 UNREGISTER_INI_ENTRIES();
435 accel_shutdown();
436 return SUCCESS;
437 }
438
439 void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
440 {
441 php_info_print_table_start();
442
443 if (
444 #ifdef HAVE_OPCACHE_FILE_CACHE
445 (ZCG(accelerator_enabled) || file_cache_only)
446 #else
447 (ZCG(accelerator_enabled))
448 #endif
449 ) {
450 php_info_print_table_row(2, "Opcode Caching", "Up and Running");
451 } else {
452 php_info_print_table_row(2, "Opcode Caching", "Disabled");
453 }
454 if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).optimization_level) {
455 php_info_print_table_row(2, "Optimization", "Enabled");
456 } else {
457 php_info_print_table_row(2, "Optimization", "Disabled");
458 }
459 #ifdef HAVE_OPCACHE_FILE_CACHE
460 if (!file_cache_only) {
461 php_info_print_table_row(2, "SHM Cache", "Enabled");
462 } else {
463 php_info_print_table_row(2, "SHM Cache", "Disabled");
464 }
465 if (ZCG(accel_directives).file_cache) {
466 php_info_print_table_row(2, "File Cache", "Enabled");
467 } else {
468 php_info_print_table_row(2, "File Cache", "Disabled");
469 }
470 if (file_cache_only) {
471 if (!accel_startup_ok || zps_api_failure_reason) {
472 php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
473 } else {
474 php_info_print_table_row(2, "Startup", "OK");
475 }
476 } else
477 #endif
478 if (ZCG(enabled)) {
479 if (!accel_startup_ok || zps_api_failure_reason) {
480 php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
481 } else {
482 char buf[32];
483 php_info_print_table_row(2, "Startup", "OK");
484 php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
485 snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(hits));
486 php_info_print_table_row(2, "Cache hits", buf);
487 snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
488 php_info_print_table_row(2, "Cache misses", buf);
489 snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
490 php_info_print_table_row(2, "Used memory", buf);
491 snprintf(buf, sizeof(buf), "%zu", zend_shared_alloc_get_free_memory());
492 php_info_print_table_row(2, "Free memory", buf);
493 snprintf(buf, sizeof(buf), "%zu", ZSMMG(wasted_shared_memory));
494 php_info_print_table_row(2, "Wasted memory", buf);
495 if (ZCSG(interned_strings_start) && ZCSG(interned_strings_end) && ZCSG(interned_strings_top)) {
496 snprintf(buf, sizeof(buf), "%td", ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
497 php_info_print_table_row(2, "Interned Strings Used memory", buf);
498 snprintf(buf, sizeof(buf), "%td", ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
499 php_info_print_table_row(2, "Interned Strings Free memory", buf);
500 }
501 snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).num_direct_entries);
502 php_info_print_table_row(2, "Cached scripts", buf);
503 snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).num_entries);
504 php_info_print_table_row(2, "Cached keys", buf);
505 snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).max_num_entries);
506 php_info_print_table_row(2, "Max keys", buf);
507 snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(oom_restarts));
508 php_info_print_table_row(2, "OOM restarts", buf);
509 snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(hash_restarts));
510 php_info_print_table_row(2, "Hash keys restarts", buf);
511 snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(manual_restarts));
512 php_info_print_table_row(2, "Manual restarts", buf);
513 }
514 }
515
516 php_info_print_table_end();
517 DISPLAY_INI_ENTRIES();
518 }
519
520 static zend_module_entry accel_module_entry = {
521 STANDARD_MODULE_HEADER,
522 ACCELERATOR_PRODUCT_NAME,
523 accel_functions,
524 ZEND_MINIT(zend_accelerator),
525 ZEND_MSHUTDOWN(zend_accelerator),
526 NULL,
527 NULL,
528 zend_accel_info,
529 PHP_VERSION,
530 NO_MODULE_GLOBALS,
531 accel_post_deactivate,
532 STANDARD_MODULE_PROPERTIES_EX
533 };
534
535 int start_accel_module(void)
536 {
537 return zend_startup_module(&accel_module_entry);
538 }
539
540 /* {{{ proto array accelerator_get_scripts()
541 Get the scripts which are accelerated by ZendAccelerator */
542 static int accelerator_get_scripts(zval *return_value)
543 {
544 uint32_t i;
545 zval persistent_script_report;
546 zend_accel_hash_entry *cache_entry;
547 struct tm *ta;
548 struct timeval exec_time;
549 struct timeval fetch_time;
550
551 if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
552 return 0;
553 }
554
555 array_init(return_value);
556 for (i = 0; i<ZCSG(hash).max_num_entries; i++) {
557 for (cache_entry = ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) {
558 zend_persistent_script *script;
559 char *str;
560 size_t len;
561
562 if (cache_entry->indirect) continue;
563
564 script = (zend_persistent_script *)cache_entry->data;
565
566 array_init(&persistent_script_report);
567 add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->script.filename, 0));
568 add_assoc_long(&persistent_script_report, "hits", (zend_long)script->dynamic_members.hits);
569 add_assoc_long(&persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
570 ta = localtime(&script->dynamic_members.last_used);
571 str = asctime(ta);
572 len = strlen(str);
573 if (len > 0 && str[len - 1] == '\n') len--;
574 add_assoc_stringl(&persistent_script_report, "last_used", str, len);
575 add_assoc_long(&persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used);
576 if (ZCG(accel_directives).validate_timestamps) {
577 add_assoc_long(&persistent_script_report, "timestamp", (zend_long)script->timestamp);
578 }
579 timerclear(&exec_time);
580 timerclear(&fetch_time);
581
582 zend_hash_str_update(Z_ARRVAL_P(return_value), cache_entry->key, cache_entry->key_length, &persistent_script_report);
583 }
584 }
585 accelerator_shm_read_unlock();
586
587 return 1;
588 }
589
590 /* {{{ proto array accelerator_get_status([bool fetch_scripts])
591 Obtain statistics information regarding code acceleration */
592 static ZEND_FUNCTION(opcache_get_status)
593 {
594 zend_long reqs;
595 zval memory_usage, statistics, scripts;
596 zend_bool fetch_scripts = 1;
597
598 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &fetch_scripts) == FAILURE) {
599 return;
600 }
601
602 if (!validate_api_restriction()) {
603 RETURN_FALSE;
604 }
605
606 if (!accel_startup_ok) {
607 RETURN_FALSE;
608 }
609
610 array_init(return_value);
611
612 /* Trivia */
613 add_assoc_bool(return_value, "opcache_enabled", ZCG(accelerator_enabled));
614
615 #ifdef HAVE_OPCACHE_FILE_CACHE
616 if (ZCG(accel_directives).file_cache) {
617 add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache);
618 }
619 if (file_cache_only) {
620 add_assoc_bool(return_value, "file_cache_only", 1);
621 return;
622 }
623 #endif
624
625 add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
626 add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending));
627 add_assoc_bool(return_value, "restart_in_progress", ZCSG(restart_in_progress));
628
629 /* Memory usage statistics */
630 array_init(&memory_usage);
631 add_assoc_long(&memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
632 add_assoc_long(&memory_usage, "free_memory", zend_shared_alloc_get_free_memory());
633 add_assoc_long(&memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory));
634 add_assoc_double(&memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0);
635 add_assoc_zval(return_value, "memory_usage", &memory_usage);
636
637 if (ZCSG(interned_strings_start) && ZCSG(interned_strings_end) && ZCSG(interned_strings_top)) {
638 zval interned_strings_usage;
639
640 array_init(&interned_strings_usage);
641 add_assoc_long(&interned_strings_usage, "buffer_size", ZCSG(interned_strings_end) - ZCSG(interned_strings_start));
642 add_assoc_long(&interned_strings_usage, "used_memory", ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
643 add_assoc_long(&interned_strings_usage, "free_memory", ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
644 add_assoc_long(&interned_strings_usage, "number_of_strings", ZCSG(interned_strings).nNumOfElements);
645 add_assoc_zval(return_value, "interned_strings_usage", &interned_strings_usage);
646 }
647
648 /* Accelerator statistics */
649 array_init(&statistics);
650 add_assoc_long(&statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries);
651 add_assoc_long(&statistics, "num_cached_keys", ZCSG(hash).num_entries);
652 add_assoc_long(&statistics, "max_cached_keys", ZCSG(hash).max_num_entries);
653 add_assoc_long(&statistics, "hits", (zend_long)ZCSG(hits));
654 add_assoc_long(&statistics, "start_time", ZCSG(start_time));
655 add_assoc_long(&statistics, "last_restart_time", ZCSG(last_restart_time));
656 add_assoc_long(&statistics, "oom_restarts", ZCSG(oom_restarts));
657 add_assoc_long(&statistics, "hash_restarts", ZCSG(hash_restarts));
658 add_assoc_long(&statistics, "manual_restarts", ZCSG(manual_restarts));
659 add_assoc_long(&statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
660 add_assoc_long(&statistics, "blacklist_misses", ZCSG(blacklist_misses));
661 reqs = ZCSG(hits)+ZCSG(misses);
662 add_assoc_double(&statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0);
663 add_assoc_double(&statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0);
664 add_assoc_zval(return_value, "opcache_statistics", &statistics);
665
666 if (fetch_scripts) {
667 /* accelerated scripts */
668 if (accelerator_get_scripts(&scripts)) {
669 add_assoc_zval(return_value, "scripts", &scripts);
670 }
671 }
672 }
673
674 static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value)
675 {
676 add_next_index_stringl(return_value, p->path, p->path_length);
677 return 0;
678 }
679
680 /* {{{ proto array accelerator_get_configuration()
681 Obtain configuration information */
682 static ZEND_FUNCTION(opcache_get_configuration)
683 {
684 zval directives, version, blacklist;
685
686 if (zend_parse_parameters_none() == FAILURE) {
687 RETURN_FALSE;
688 }
689
690 if (!validate_api_restriction()) {
691 RETURN_FALSE;
692 }
693
694 array_init(return_value);
695
696 /* directives */
697 array_init(&directives);
698 add_assoc_bool(&directives, "opcache.enable", ZCG(enabled));
699 add_assoc_bool(&directives, "opcache.enable_cli", ZCG(accel_directives).enable_cli);
700 add_assoc_bool(&directives, "opcache.use_cwd", ZCG(accel_directives).use_cwd);
701 add_assoc_bool(&directives, "opcache.validate_timestamps", ZCG(accel_directives).validate_timestamps);
702 add_assoc_bool(&directives, "opcache.validate_permission", ZCG(accel_directives).validate_permission);
703 #ifndef ZEND_WIN32
704 add_assoc_bool(&directives, "opcache.validate_root", ZCG(accel_directives).validate_root);
705 #endif
706 add_assoc_bool(&directives, "opcache.inherited_hack", ZCG(accel_directives).inherited_hack);
707 add_assoc_bool(&directives, "opcache.dups_fix", ZCG(accel_directives).ignore_dups);
708 add_assoc_bool(&directives, "opcache.revalidate_path", ZCG(accel_directives).revalidate_path);
709
710 add_assoc_long(&directives, "opcache.log_verbosity_level", ZCG(accel_directives).log_verbosity_level);
711 add_assoc_long(&directives, "opcache.memory_consumption", ZCG(accel_directives).memory_consumption);
712 add_assoc_long(&directives, "opcache.interned_strings_buffer",ZCG(accel_directives).interned_strings_buffer);
713 add_assoc_long(&directives, "opcache.max_accelerated_files", ZCG(accel_directives).max_accelerated_files);
714 add_assoc_double(&directives, "opcache.max_wasted_percentage", ZCG(accel_directives).max_wasted_percentage);
715 add_assoc_long(&directives, "opcache.consistency_checks", ZCG(accel_directives).consistency_checks);
716 add_assoc_long(&directives, "opcache.force_restart_timeout", ZCG(accel_directives).force_restart_timeout);
717 add_assoc_long(&directives, "opcache.revalidate_freq", ZCG(accel_directives).revalidate_freq);
718 add_assoc_string(&directives, "opcache.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model));
719 add_assoc_string(&directives, "opcache.blacklist_filename", STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename));
720 add_assoc_long(&directives, "opcache.max_file_size", ZCG(accel_directives).max_file_size);
721 add_assoc_string(&directives, "opcache.error_log", STRING_NOT_NULL(ZCG(accel_directives).error_log));
722
723 add_assoc_bool(&directives, "opcache.protect_memory", ZCG(accel_directives).protect_memory);
724 add_assoc_bool(&directives, "opcache.save_comments", ZCG(accel_directives).save_comments);
725 add_assoc_bool(&directives, "opcache.enable_file_override", ZCG(accel_directives).file_override_enabled);
726 add_assoc_long(&directives, "opcache.optimization_level", ZCG(accel_directives).optimization_level);
727
728 #ifndef ZEND_WIN32
729 add_assoc_string(&directives, "opcache.lockfile_path", STRING_NOT_NULL(ZCG(accel_directives).lockfile_path));
730 #else
731 add_assoc_string(&directives, "opcache.mmap_base", STRING_NOT_NULL(ZCG(accel_directives).mmap_base));
732 #endif
733
734 #ifdef HAVE_OPCACHE_FILE_CACHE
735 add_assoc_string(&directives, "opcache.file_cache", ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : "");
736 add_assoc_bool(&directives, "opcache.file_cache_only", ZCG(accel_directives).file_cache_only);
737 add_assoc_bool(&directives, "opcache.file_cache_consistency_checks", ZCG(accel_directives).file_cache_consistency_checks);
738 #endif
739 #if ENABLE_FILE_CACHE_FALLBACK
740 add_assoc_bool(&directives, "opcache.file_cache_fallback", ZCG(accel_directives).file_cache_fallback);
741 #endif
742
743 add_assoc_long(&directives, "opcache.file_update_protection", ZCG(accel_directives).file_update_protection);
744 add_assoc_long(&directives, "opcache.opt_debug_level", ZCG(accel_directives).opt_debug_level);
745 add_assoc_string(&directives, "opcache.restrict_api", STRING_NOT_NULL(ZCG(accel_directives).restrict_api));
746 #ifdef HAVE_HUGE_CODE_PAGES
747 add_assoc_bool(&directives, "opcache.huge_code_pages", ZCG(accel_directives).huge_code_pages);
748 #endif
749
750 add_assoc_zval(return_value, "directives", &directives);
751
752 /*version */
753 array_init(&version);
754 add_assoc_string(&version, "version", PHP_VERSION);
755 add_assoc_string(&version, "opcache_product_name", ACCELERATOR_PRODUCT_NAME);
756 add_assoc_zval(return_value, "version", &version);
757
758 /* blacklist */
759 array_init(&blacklist);
760 zend_accel_blacklist_apply(&accel_blacklist, add_blacklist_path, &blacklist);
761 add_assoc_zval(return_value, "blacklist", &blacklist);
762 }
763
764 /* {{{ proto void accelerator_reset()
765 Request that the contents of the opcode cache to be reset */
766 static ZEND_FUNCTION(opcache_reset)
767 {
768 if (zend_parse_parameters_none() == FAILURE) {
769 RETURN_FALSE;
770 }
771
772 if (!validate_api_restriction()) {
773 RETURN_FALSE;
774 }
775
776 if ((!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled))
777 #if ENABLE_FILE_CACHE_FALLBACK
778 && !fallback_process
779 #endif
780 ) {
781 RETURN_FALSE;
782 }
783
784 /* exclusive lock */
785 zend_shared_alloc_lock();
786 zend_accel_schedule_restart(ACCEL_RESTART_USER);
787 zend_shared_alloc_unlock();
788 RETURN_TRUE;
789 }
790
791 /* {{{ proto void opcache_invalidate(string $script [, bool $force = false])
792 Invalidates cached script (in necessary or forced) */
793 static ZEND_FUNCTION(opcache_invalidate)
794 {
795 char *script_name;
796 size_t script_name_len;
797 zend_bool force = 0;
798
799 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &script_name, &script_name_len, &force) == FAILURE) {
800 return;
801 }
802
803 if (!validate_api_restriction()) {
804 RETURN_FALSE;
805 }
806
807 if (zend_accel_invalidate(script_name, script_name_len, force) == SUCCESS) {
808 RETURN_TRUE;
809 } else {
810 RETURN_FALSE;
811 }
812 }
813
814 static ZEND_FUNCTION(opcache_compile_file)
815 {
816 char *script_name;
817 size_t script_name_len;
818 zend_file_handle handle;
819 zend_op_array *op_array = NULL;
820 zend_execute_data *orig_execute_data = NULL;
821
822 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &script_name, &script_name_len) == FAILURE) {
823 return;
824 }
825
826 if (!ZCG(accelerator_enabled)) {
827 zend_error(E_NOTICE, ACCELERATOR_PRODUCT_NAME " seems to be disabled, can't compile file");
828 RETURN_FALSE;
829 }
830
831 handle.filename = script_name;
832 handle.free_filename = 0;
833 handle.opened_path = NULL;
834 handle.type = ZEND_HANDLE_FILENAME;
835
836 orig_execute_data = EG(current_execute_data);
837
838 zend_try {
839 op_array = persistent_compile_file(&handle, ZEND_INCLUDE);
840 } zend_catch {
841 EG(current_execute_data) = orig_execute_data;
842 zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename);
843 } zend_end_try();
844
845 if(op_array != NULL) {
846 destroy_op_array(op_array);
847 efree(op_array);
848 RETVAL_TRUE;
849 } else {
850 RETVAL_FALSE;
851 }
852 zend_destroy_file_handle(&handle);
853 }
854
855 /* {{{ proto bool opcache_is_script_cached(string $script)
856 Return true if the script is cached in OPCache, false if it is not cached or if OPCache is not running. */
857 static ZEND_FUNCTION(opcache_is_script_cached)
858 {
859 zend_string *script_name;
860
861 if (!validate_api_restriction()) {
862 RETURN_FALSE;
863 }
864
865 if (!ZCG(accelerator_enabled)) {
866 RETURN_FALSE;
867 }
868
869 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &script_name) == FAILURE) {
870 return;
871 }
872
873 RETURN_BOOL(filename_is_in_cache(script_name));
874 }
875