mirror of
https://github.com/php/pecl-caching-apc.git
synced 2026-03-23 22:42:11 +01:00
283 lines
9.9 KiB
C
283 lines
9.9 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| APC |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2006-2011 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
|
|
+----------------------------------------------------------------------+
|
|
|
|
This software was contributed to PHP by Community Connect Inc. in 2002
|
|
and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
|
|
Future revisions and derivatives of this source code must acknowledge
|
|
Community Connect Inc. as the original contributor of this module by
|
|
leaving this note intact in the source code.
|
|
|
|
All other licensing and usage conditions are those of the PHP Group.
|
|
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "apc_zend.h"
|
|
#include "apc_globals.h"
|
|
|
|
/* true global */
|
|
int apc_reserved_offset;
|
|
|
|
void* apc_php_malloc(size_t n TSRMLS_DC)
|
|
{
|
|
return emalloc(n);
|
|
}
|
|
|
|
void apc_php_free(void* p TSRMLS_DC)
|
|
{
|
|
efree(p);
|
|
}
|
|
|
|
#ifdef APC_OPCODE_OVERRIDE
|
|
|
|
static opcode_handler_t *apc_original_opcode_handlers;
|
|
static opcode_handler_t apc_opcode_handlers[APC_OPCODE_HANDLER_COUNT];
|
|
|
|
#ifdef ZEND_ENGINE_2_5
|
|
#define APC_EX_CV(i) (*EX_CV_NUM(execute_data, i))
|
|
#define APC_EX_T(offset) (*EX_TMP_VAR(execute_data, offset))
|
|
#else
|
|
#define APC_EX_CV(i) execute_data->CVs[i]
|
|
#define APC_EX_T(offset) (*(temp_variable *)((char*)execute_data->Ts + offset))
|
|
#endif
|
|
|
|
#ifdef ZEND_ENGINE_2_4
|
|
static zval *apc_get_zval_ptr(zend_uchar op_type, znode_op *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC)
|
|
{
|
|
*freeval = NULL;
|
|
|
|
switch (op_type) {
|
|
case IS_CONST:
|
|
return node->zv;
|
|
case IS_VAR:
|
|
return APC_EX_T(node->var).var.ptr;
|
|
case IS_TMP_VAR:
|
|
return (*freeval = &APC_EX_T(node->var).tmp_var);
|
|
case IS_CV:
|
|
{
|
|
zval ***ret = &APC_EX_CV(node->var);
|
|
|
|
if (!*ret) {
|
|
zend_compiled_variable *cv = &EG(active_op_array)->vars[node->var];
|
|
|
|
if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) {
|
|
apc_notice("Undefined variable: %s" TSRMLS_CC, cv->name);
|
|
return &EG(uninitialized_zval);
|
|
}
|
|
}
|
|
return **ret;
|
|
}
|
|
case IS_UNUSED:
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
#else
|
|
static zval *apc_get_zval_ptr(znode *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC)
|
|
{
|
|
*freeval = NULL;
|
|
|
|
switch (node->op_type) {
|
|
case IS_CONST:
|
|
return &(node->u.constant);
|
|
case IS_VAR:
|
|
return APC_EX_T(node->u.var).var.ptr;
|
|
case IS_TMP_VAR:
|
|
return (*freeval = &APC_EX_T(node->u.var).tmp_var);
|
|
#ifdef ZEND_ENGINE_2_1
|
|
case IS_CV:
|
|
{
|
|
zval ***ret = &APC_EX_CV(node->u.var);
|
|
|
|
if (!*ret) {
|
|
zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var];
|
|
|
|
if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) {
|
|
apc_notice("Undefined variable: %s" TSRMLS_CC, cv->name);
|
|
return &EG(uninitialized_zval);
|
|
}
|
|
}
|
|
return **ret;
|
|
}
|
|
#endif
|
|
case IS_UNUSED:
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int ZEND_FASTCALL apc_op_ZEND_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS)
|
|
{
|
|
APC_ZEND_OPLINE
|
|
zval *freeop1 = NULL;
|
|
zval *inc_filename = NULL, tmp_inc_filename;
|
|
php_stream_wrapper *wrapper;
|
|
char *path_for_open, realpath_storage[MAXPATHLEN], *real_path;
|
|
int ret = 0;
|
|
zend_file_handle file_handle;
|
|
apc_cache_entry_t* cache_entry;
|
|
|
|
#ifdef ZEND_ENGINE_2_4
|
|
if (opline->extended_value != ZEND_INCLUDE_ONCE &&
|
|
opline->extended_value != ZEND_REQUIRE_ONCE) {
|
|
return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
}
|
|
|
|
inc_filename = apc_get_zval_ptr(opline->op1_type, &opline->op1, &freeop1, execute_data TSRMLS_CC);
|
|
#else
|
|
if (Z_LVAL(opline->op2.u.constant) != ZEND_INCLUDE_ONCE &&
|
|
Z_LVAL(opline->op2.u.constant) != ZEND_REQUIRE_ONCE) {
|
|
return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
}
|
|
|
|
inc_filename = apc_get_zval_ptr(&opline->op1, &freeop1, execute_data TSRMLS_CC);
|
|
#endif
|
|
|
|
if (Z_TYPE_P(inc_filename) != IS_STRING) {
|
|
tmp_inc_filename = *inc_filename;
|
|
zval_copy_ctor(&tmp_inc_filename);
|
|
convert_to_string(&tmp_inc_filename);
|
|
inc_filename = &tmp_inc_filename;
|
|
}
|
|
|
|
/* Check to see if this is a regular file and bail if it isn't */
|
|
wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(inc_filename), &path_for_open, 0 TSRMLS_CC);
|
|
if (wrapper != &php_plain_files_wrapper) {
|
|
if (inc_filename == &tmp_inc_filename) {
|
|
zval_dtor(&tmp_inc_filename);
|
|
}
|
|
return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
}
|
|
|
|
/*
|
|
Before we do anything else, we can check to see if we have a cached version of this
|
|
file, in which case we can skip the realpath - this also means that we will be checking
|
|
the correct realpath for this cache entry since it is possible that the inode existed in
|
|
a different directory at the time it was cached. Some deploy systems will do tricky things
|
|
where they rename directories and move a symlink around.
|
|
*/
|
|
file_handle.filename = inc_filename->value.str.val;
|
|
file_handle.free_filename = 0;
|
|
file_handle.type = ZEND_HANDLE_FILENAME;
|
|
file_handle.opened_path = NULL;
|
|
file_handle.handle.fp = NULL;
|
|
cache_entry = apc_get_cache_entry(&file_handle TSRMLS_CC);
|
|
if (cache_entry) {
|
|
real_path = cache_entry->data.file.filename;
|
|
} else {
|
|
if (!IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) || !VCWD_REALPATH(path_for_open, realpath_storage)) {
|
|
/* Fallback to original handler */
|
|
if (inc_filename == &tmp_inc_filename) {
|
|
zval_dtor(&tmp_inc_filename);
|
|
}
|
|
return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
}
|
|
real_path = realpath_storage;
|
|
}
|
|
|
|
if (zend_hash_exists(&EG(included_files), real_path, strlen(real_path) + 1)) {
|
|
#ifdef ZEND_ENGINE_2_4
|
|
if (!(opline->result_type & EXT_TYPE_UNUSED)) {
|
|
ALLOC_INIT_ZVAL(APC_EX_T(opline->result.var).var.ptr);
|
|
ZVAL_TRUE(APC_EX_T(opline->result.var).var.ptr);
|
|
}
|
|
#else
|
|
if (!(opline->result.u.EA.type & EXT_TYPE_UNUSED)) {
|
|
ALLOC_INIT_ZVAL(APC_EX_T(opline->result.u.var).var.ptr);
|
|
ZVAL_TRUE(APC_EX_T(opline->result.u.var).var.ptr);
|
|
}
|
|
#endif
|
|
if (inc_filename == &tmp_inc_filename) {
|
|
zval_dtor(&tmp_inc_filename);
|
|
}
|
|
if (freeop1) {
|
|
zval_dtor(freeop1);
|
|
}
|
|
execute_data->opline++;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef ZEND_ENGINE_2_4
|
|
opline->extended_value = (opline->extended_value == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE;
|
|
ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
opline->extended_value = (opline->extended_value == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE;
|
|
#else
|
|
Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE;
|
|
ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
|
Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
void apc_zend_init(TSRMLS_D)
|
|
{
|
|
zend_extension dummy_ext;
|
|
apc_reserved_offset = zend_get_resource_handle(&dummy_ext);
|
|
assert(apc_reserved_offset == dummy_ext.resource_number);
|
|
assert(apc_reserved_offset != -1);
|
|
assert(sizeof(apc_opflags_t) <= sizeof(void*));
|
|
if (!APCG(include_once)) {
|
|
/* If we're not overriding the INCLUDE_OR_EVAL handler, then just skip this malarkey */
|
|
return;
|
|
}
|
|
|
|
memcpy(apc_opcode_handlers, zend_opcode_handlers, sizeof(apc_opcode_handlers));
|
|
|
|
/* 5.0 exposes zend_opcode_handlers differently than 5.1 and later */
|
|
#ifdef ZEND_ENGINE_2_1
|
|
apc_original_opcode_handlers = zend_opcode_handlers;
|
|
zend_opcode_handlers = apc_opcode_handlers;
|
|
#else
|
|
apc_original_opcode_handlers = apc_opcode_handlers;
|
|
#endif
|
|
|
|
APC_REPLACE_OPCODE(ZEND_INCLUDE_OR_EVAL);
|
|
}
|
|
|
|
void apc_zend_shutdown(TSRMLS_D)
|
|
{
|
|
if (!APCG(include_once)) {
|
|
/* Nothing changed, nothing to restore */
|
|
return;
|
|
}
|
|
|
|
#ifdef ZEND_ENGINE_2_1
|
|
zend_opcode_handlers = apc_original_opcode_handlers;
|
|
#else
|
|
memcpy(zend_opcode_handlers, apc_original_opcode_handlers, sizeof(apc_opcode_handlers));
|
|
#endif
|
|
}
|
|
|
|
#else /* Opcode Overrides unavailable */
|
|
|
|
void apc_zend_init(TSRMLS_D) { }
|
|
void apc_zend_shutdown(TSRMLS_D) { }
|
|
|
|
#endif /* APC_OPCODE_OVERRIDE */
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker
|
|
* vim<600: expandtab sw=4 ts=4 sts=4
|
|
*/
|