PHP Classes

File: ext/kernel/fcall.c

Recommend this page to a friend!
  Classes of Alien Fernandez   Phady Framework   ext/kernel/fcall.c   Download  
File: ext/kernel/fcall.c
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Phady Framework
Web application MVC framework based on Phalcon
Author: By
Last change:
Date: 7 years ago
Size: 39,137 bytes
 

Contents

Class file image Download
/* +------------------------------------------------------------------------+ | Zephir Language | +------------------------------------------------------------------------+ | Copyright (c) 2011-2015 Zephir Team (http://www.zephir-lang.com) | +------------------------------------------------------------------------+ | This source file is subject to the New BSD License that is bundled | | with this package in the file docs/LICENSE.txt. | | | | If you did not receive a copy of the license and are unable to | | obtain it through the world-wide-web, please send an email | | to license@zephir-lang.com so we can send you a copy immediately. | +------------------------------------------------------------------------+ | Authors: Andres Gutierrez <andres@zephir-lang.com> | | Eduar Carvajal <eduar@zephir-lang.com> | | Vladimir Kolesnikov <vladimir@extrememember.com> | +------------------------------------------------------------------------+ */ #include <php.h> #include "php_ext.h" #include <Zend/zend_API.h> #include <Zend/zend_exceptions.h> #include <Zend/zend_execute.h> #include "kernel/main.h" #include "kernel/fcall.h" #include "kernel/extended/fcall.h" #include "kernel/memory.h" #include "kernel/hash.h" #include "kernel/operators.h" #include "kernel/exception.h" #include "kernel/backtrace.h" #if PHP_VERSION_ID >= 50500 static const unsigned char tolower_map[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; #endif int zephir_has_constructor_ce(const zend_class_entry *ce) { while (ce) { if (ce->constructor) { return 1; } ce = ce->parent; } return 0; } #if 0 static inline ulong zephir_update_hash(const char *arKey, uint nKeyLength, ulong hash) { for (; nKeyLength >= 8; nKeyLength -= 8) { hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; } switch (nKeyLength) { case 7: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 6: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 5: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 4: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 3: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 2: hash = ((hash << 5) + hash) + *arKey++; /* no break */ case 1: hash = ((hash << 5) + hash) + *arKey++; /* no break */ default: break; } return hash; } #endif static char *zephir_fcall_possible_method(zend_class_entry *ce, const char *wrong_name TSRMLS_DC) { HashTable *methods; HashPosition pos; zend_function *method; char *possible_method = NULL; zval *left = NULL, *right = NULL, method_name; zval *params[1]; int count; count = zend_hash_num_elements(&ce->function_table); if (count > 0) { ZEPHIR_SINIT_VAR(method_name); ZVAL_STRING(&method_name, wrong_name, 0); params[0] = &method_name; zephir_call_func_aparams(&right, SL("metaphone"), NULL, 0, 1, params TSRMLS_CC); methods = &ce->function_table; zend_hash_internal_pointer_reset_ex(methods, &pos); while (zend_hash_get_current_data_ex(methods, (void **) &method, &pos) == SUCCESS) { ZEPHIR_SINIT_VAR(method_name); ZVAL_STRING(&method_name, method->common.function_name, 0); if (left) { zephir_ptr_dtor(&left); } left = NULL; params[0] = &method_name; zephir_call_func_aparams(&left, SL("metaphone"), NULL, 0, 1, params TSRMLS_CC); if (zephir_is_equal(left, right TSRMLS_CC)) { possible_method = (char *) method->common.function_name; break; } zend_hash_move_forward_ex(methods, &pos); } if (left) { zephir_ptr_dtor(&left); } if (right) { zephir_ptr_dtor(&right); } } return possible_method; } /** * Creates a unique key to cache the current method/function call address for the current scope */ static ulong zephir_make_fcall_key(char **result, size_t *length, const zend_class_entry *obj_ce, zephir_call_type type, zval *function_name TSRMLS_DC) { const zend_class_entry *calling_scope = EG(scope); char *buf = NULL, *c; size_t l = 0, len = 0; const size_t ppzce_size = sizeof(zend_class_entry**); ulong hash = 5381; *result = NULL; *length = 0; if (calling_scope && type == zephir_fcall_parent) { calling_scope = calling_scope->parent; if (UNEXPECTED(!calling_scope)) { return 0; } } else if (type == zephir_fcall_static) { calling_scope = EG(called_scope); if (UNEXPECTED(!calling_scope)) { return 0; } } if ( calling_scope && obj_ce && calling_scope != obj_ce && !instanceof_function(obj_ce, calling_scope TSRMLS_CC) && !instanceof_function(calling_scope, obj_ce TSRMLS_CC) ) { calling_scope = NULL; } if (Z_TYPE_P(function_name) == IS_STRING) { l = (size_t)(Z_STRLEN_P(function_name)) + 1; c = Z_STRVAL_P(function_name); len = 2 * ppzce_size + l + 1; buf = emalloc(len); memcpy(buf, c, l); memcpy(buf + l, &calling_scope, ppzce_size); memcpy(buf + l + ppzce_size, &obj_ce, ppzce_size); buf[len - 1] = '\0'; } else if (Z_TYPE_P(function_name) == IS_ARRAY) { zval **method; HashTable *function_hash = Z_ARRVAL_P(function_name); if ( function_hash->nNumOfElements == 2 && zend_hash_index_find(function_hash, 1, (void**)&method) == SUCCESS && Z_TYPE_PP(method) == IS_STRING ) { l = (size_t)(Z_STRLEN_PP(method)) + 1; c = Z_STRVAL_PP(method); len = 2 * ppzce_size + l + 1; buf = emalloc(len); memcpy(buf, c, l); memcpy(buf + l, &calling_scope, ppzce_size); memcpy(buf + l + ppzce_size, &obj_ce, ppzce_size); buf[len - 1] = '\0'; } } if (EXPECTED(buf != NULL)) { size_t i; for (i = 0; i < l; ++i) { char c = buf[i]; #if PHP_VERSION_ID >= 50500 c = tolower_map[(unsigned char)c]; #else c = tolower(c); #endif buf[i] = c; hash = (hash << 5) + hash + c; } for (i = l; i < len; ++i) { char c = buf[i]; hash = (hash << 5) + hash + c; } } *result = buf; *length = len; return hash; } /** * Creates a unique key to cache the current method/function call address for the current scope */ static ulong zephir_make_fcall_info_key(char **result, size_t *length, const zend_class_entry *obj_ce, zephir_call_type type, zephir_fcall_info *info TSRMLS_DC) { const zend_class_entry *calling_scope = EG(scope); char *buf = NULL, *c; size_t l = 0, len = 0; const size_t ppzce_size = sizeof(zend_class_entry**); ulong hash = 5381; *result = NULL; *length = 0; if (calling_scope && type == zephir_fcall_parent) { calling_scope = calling_scope->parent; if (UNEXPECTED(!calling_scope)) { return 0; } } else if (type == zephir_fcall_static) { calling_scope = EG(called_scope); if (UNEXPECTED(!calling_scope)) { return 0; } } if ( calling_scope && obj_ce && calling_scope != obj_ce && !instanceof_function(obj_ce, calling_scope TSRMLS_CC) && !instanceof_function(calling_scope, obj_ce TSRMLS_CC) ) { calling_scope = NULL; } switch (info->type) { case ZEPHIR_FCALL_TYPE_FUNC: l = (size_t)(info->func_length) + 1; c = (char*) info->func_name; len = 2 * ppzce_size + l + 1; buf = emalloc(len); memcpy(buf, c, l); memcpy(buf + l, &calling_scope, ppzce_size); memcpy(buf + l + ppzce_size, &obj_ce, ppzce_size); buf[len - 1] = '\0'; break; case ZEPHIR_FCALL_TYPE_CLASS_SELF_METHOD: case ZEPHIR_FCALL_TYPE_CLASS_STATIC_METHOD: case ZEPHIR_FCALL_TYPE_CLASS_PARENT_METHOD: l = (size_t)(info->func_length) + 2; /* reserve 1 char for fcall-type */ c = (char*) info->func_name; len = 2 * ppzce_size + l + 1; buf = emalloc(len); buf[0] = info->type; memcpy(buf + 1, c, l - 1); memcpy(buf + l, &calling_scope, ppzce_size); memcpy(buf + l + ppzce_size, &obj_ce, ppzce_size); buf[len - 1] = '\0'; break; case ZEPHIR_FCALL_TYPE_CE_METHOD: case ZEPHIR_FCALL_TYPE_ZVAL_METHOD: l = (size_t)(info->func_length) + 1; c = (char*) info->func_name; len = 2 * ppzce_size + l + 1; buf = emalloc(len); memcpy(buf, c, l); memcpy(buf + l, &calling_scope, ppzce_size); memcpy(buf + l + ppzce_size, &obj_ce, ppzce_size); buf[len - 1] = '\0'; break; } if (EXPECTED(buf != NULL)) { size_t i; for (i = 0; i < l; ++i) { char c = buf[i]; #if PHP_VERSION_ID >= 50500 c = tolower_map[(unsigned char)c]; #else c = tolower(c); #endif buf[i] = c; hash = (hash << 5) + hash + c; } for (i = l; i < len; ++i) { char c = buf[i]; hash = (hash << 5) + hash + c; } } *result = buf; *length = len; return hash; } ZEPHIR_ATTR_NONNULL static void zephir_fcall_populate_fci_cache(zend_fcall_info_cache *fcic, zend_fcall_info *fci, zephir_call_type type TSRMLS_DC) { switch (type) { case zephir_fcall_parent: if (EG(scope) && EG(scope)->parent) { fcic->calling_scope = EG(scope)->parent; fcic->called_scope = EG(called_scope); fcic->object_ptr = fci->object_ptr ? fci->object_ptr : EG(This); fcic->initialized = 1; } break; case zephir_fcall_self: if (EG(scope)) { fcic->calling_scope = EG(scope); fcic->called_scope = EG(called_scope); fcic->object_ptr = fci->object_ptr ? fci->object_ptr : EG(This); fcic->initialized = 1; } break; case zephir_fcall_static: if (EG(called_scope)) { fcic->calling_scope = EG(called_scope); fcic->called_scope = EG(called_scope); fcic->object_ptr = fci->object_ptr ? fci->object_ptr : EG(This); fcic->initialized = 1; } break; case zephir_fcall_function: fcic->calling_scope = NULL; fcic->called_scope = NULL; fcic->object_ptr = NULL; fcic->initialized = 1; break; case zephir_fcall_ce: { zend_class_entry *scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL; fcic->initialized = 1; fcic->calling_scope = EG(scope); fcic->object_ptr = NULL; if (scope && EG(This) && instanceof_function(Z_OBJCE_P(EG(This)), scope TSRMLS_CC) && instanceof_function(scope, fcic->calling_scope TSRMLS_CC)) { fcic->object_ptr = EG(This); fcic->called_scope = Z_OBJCE_P(fcic->object_ptr); } else { fcic->called_scope = fcic->calling_scope; } break; } case zephir_fcall_method: fcic->initialized = 1; fcic->calling_scope = EG(scope); fcic->object_ptr = fci->object_ptr; if (fci->object_ptr) { fcic->called_scope = Z_OBJCE_P(fci->object_ptr); } else if (EG(scope) && !(EG(called_scope) && instanceof_function(EG(called_scope), EG(scope) TSRMLS_CC))) { fcic->called_scope = EG(scope); } else { fcic->called_scope = EG(called_scope); } break; default: #ifndef ZEPHIR_RELEASE fprintf(stderr, "%s: unknown call type (%d)\n", __func__, (int) type); abort(); #endif fcic->initialized = 0; /* not strictly necessary but just to be safe */ break; } } /** * Calls a function/method in the PHP userland */ int zephir_call_user_function(zval **object_pp, zend_class_entry *obj_ce, zephir_call_type type, zval *function_name, zval **retval_ptr_ptr, zephir_fcall_cache_entry **cache_entry, int cache_slot, zend_uint param_count, zval *params[], zephir_fcall_info *info TSRMLS_DC) { zval ***params_ptr, ***params_array = NULL; zval **static_params_array[10]; zval *local_retval_ptr = NULL; int status; zend_fcall_info fci; zend_fcall_info_cache fcic /* , clone */; zend_zephir_globals_def *zephir_globals_ptr = ZEPHIR_VGLOBAL; char *fcall_key = NULL; size_t fcall_key_len; ulong fcall_key_hash; zephir_fcall_cache_entry **temp_cache_entry = NULL; zend_class_entry *old_scope = EG(scope); int reload_cache = 1; assert(obj_ce || !object_pp); if (retval_ptr_ptr && *retval_ptr_ptr) { zval_ptr_dtor(retval_ptr_ptr); *retval_ptr_ptr = NULL; } ++zephir_globals_ptr->recursive_lock; if (UNEXPECTED(zephir_globals_ptr->recursive_lock > 2048)) { zend_error(E_ERROR, "Maximum recursion depth exceeded"); return FAILURE; } if (param_count) { zend_uint i; if (UNEXPECTED(param_count > 10)) { params_array = (zval***) emalloc(param_count * sizeof(zval**)); params_ptr = params_array; for (i = 0; i < param_count; ++i) { params_array[i] = &params[i]; } } else { params_ptr = static_params_array; for (i = 0; i < param_count; ++i) { static_params_array[i] = &params[i]; } } } else { params_ptr = NULL; } if (type != zephir_fcall_function && !object_pp) { object_pp = EG(This) ? &EG(This) : NULL; if (!obj_ce && object_pp) { obj_ce = Z_OBJCE_PP(object_pp); } } if (obj_ce) { EG(scope) = obj_ce; } if (!cache_entry || !*cache_entry) { if (zephir_globals_ptr->cache_enabled) { if (cache_slot > 0) { if (zephir_globals_ptr->scache[cache_slot]) { reload_cache = 0; temp_cache_entry = &zephir_globals_ptr->scache[cache_slot]; if (cache_entry) { *cache_entry = *temp_cache_entry; } } } if (reload_cache) { if (info) { fcall_key_hash = zephir_make_fcall_info_key(&fcall_key, &fcall_key_len, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_PP(object_pp) : obj_ce), type, info TSRMLS_CC); } else { fcall_key_hash = zephir_make_fcall_key(&fcall_key, &fcall_key_len, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_PP(object_pp) : obj_ce), type, function_name TSRMLS_CC); } } } } fci.size = sizeof(fci); fci.function_table = obj_ce ? &obj_ce->function_table : EG(function_table); fci.object_ptr = object_pp ? *object_pp : NULL; fci.function_name = function_name; fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &local_retval_ptr; fci.param_count = param_count; fci.params = params_ptr; fci.no_separation = 1; fci.symbol_table = NULL; fcic.initialized = 0; fcic.function_handler = NULL; fcic.calling_scope = NULL; fcic.called_scope = NULL; if (!cache_entry || !*cache_entry) { if (fcall_key && zend_hash_quick_find(zephir_globals_ptr->fcache, fcall_key, fcall_key_len, fcall_key_hash, (void**)&temp_cache_entry) != FAILURE) { zephir_fcall_populate_fci_cache(&fcic, &fci, type TSRMLS_CC); #ifndef ZEPHIR_RELEASE fcic.function_handler = (*temp_cache_entry)->f; ++(*temp_cache_entry)->times; #else fcic.function_handler = *temp_cache_entry; #endif /*memcpy(&clone, &fcic, sizeof(clone));*/ } } else { zephir_fcall_populate_fci_cache(&fcic, &fci, type TSRMLS_CC); #ifndef ZEPHIR_RELEASE fcic.function_handler = (*cache_entry)->f; ++(*cache_entry)->times; #else fcic.function_handler = *cache_entry; #endif } /* Xdebug fix */ //if (fcic.function_handler && fcic.function_handler->type == ZEND_INTERNAL_FUNCTION && fcic.function_handler->op_array) { // fcic.function_handler->op_array.filename = "?"; // fcic.function_handler->op_array.line_start = 0; // fcic.function_handler->op_array.line_end = 0; //} /* fcic.initialized = 0; */ #if PHP_VERSION_ID >= 50600 status = ZEPHIR_ZEND_CALL_FUNCTION_WRAPPER(&fci, &fcic, info TSRMLS_CC); #else status = ZEPHIR_ZEND_CALL_FUNCTION_WRAPPER(&fci, &fcic TSRMLS_CC); #endif /* if (fcic.initialized && cache_entry) { if (fcic.called_scope != clone.called_scope) { fprintf(stderr, "real called_scope: %s (%p)\n", fcic.called_scope->name, fcic.called_scope); fprintf(stderr, "my called_scope: %s (%p)\n", clone.called_scope->name, clone.called_scope); fprintf(stderr, "type: %d\n", (int)type); } if (fcic.calling_scope != clone.calling_scope) { fprintf(stderr, "real calling_scope: %s (%p)\n", fcic.calling_scope->name, fcic.calling_scope); fprintf(stderr, "my calling_scope: %s (%p)\n", clone.calling_scope->name, clone.calling_scope); fprintf(stderr, "type: %d\n", (int)type); } if (fcic.object_ptr != clone.object_ptr) { fprintf(stderr, "real object_ptr: %s (%p)\n", (fcic.object_ptr ? Z_OBJCE_P(fcic.object_ptr)->name : ""), fcic.object_ptr); fprintf(stderr, "my object_ptr: %s (%p)\n", (clone.object_ptr ? Z_OBJCE_P(clone.object_ptr)->name : ""), clone.object_ptr); fprintf(stderr, "type: %d\n", (int)type); } if (fcic.function_handler != clone.function_handler) { fprintf(stderr, "real handler: %p (%s::%s)\n", fcic.function_handler, (fcic.function_handler->common.scope ? fcic.function_handler->common.scope->name : ""), fcic.function_handler->common.function_name); fprintf(stderr, "my handler: %p (%s::%s)\n", clone.function_handler, (clone.function_handler->common.scope ? clone.function_handler->common.scope->name : ""), clone.function_handler->common.function_name); fprintf(stderr, "type: %d\n", (int)type); } } */ EG(scope) = old_scope; if (!cache_entry || !*cache_entry) { if (EXPECTED(status != FAILURE) && fcall_key && !temp_cache_entry && fcic.initialized) { #ifndef ZEPHIR_RELEASE zephir_fcall_cache_entry *cache_entry_temp = malloc(sizeof(zephir_fcall_cache_entry)); cache_entry_temp->f = fcic.function_handler; cache_entry_temp->times = 0; #else zephir_fcall_cache_entry *cache_entry_temp = fcic.function_handler; #endif if (FAILURE == zend_hash_quick_add(zephir_globals_ptr->fcache, fcall_key, fcall_key_len, fcall_key_hash, &cache_entry_temp, sizeof(zephir_fcall_cache_entry*), NULL)) { #ifndef ZEPHIR_RELEASE free(temp_cache_entry); #endif } else { if (cache_entry) { *cache_entry = cache_entry_temp; if (cache_slot > 0) { zephir_globals_ptr->scache[cache_slot] = *cache_entry; } } } } } if (fcall_key) { efree(fcall_key); } if (UNEXPECTED(params_array != NULL)) { efree(params_array); } if (!retval_ptr_ptr) { if (local_retval_ptr) { zval_ptr_dtor(&local_retval_ptr); } } --zephir_globals_ptr->recursive_lock; return status; } int zephir_call_func_aparams(zval **return_value_ptr, const char *func_name, uint func_length, zephir_fcall_cache_entry **cache_entry, int cache_slot, uint param_count, zval **params TSRMLS_DC) { int status; zval *rv = NULL, **rvp = return_value_ptr ? return_value_ptr : &rv; zval *func = NULL; #if PHP_VERSION_ID >= 50600 zephir_fcall_info info; #endif #ifndef ZEPHIR_RELEASE if (return_value_ptr && *return_value_ptr) { fprintf(stderr, "%s: *return_value_ptr must be NULL\n", __func__); zephir_print_backtrace(); abort(); } #endif #if PHP_VERSION_ID >= 50600 info.type = ZEPHIR_FCALL_TYPE_FUNC; info.class_name = NULL; info.func_name = func_name; info.func_length = func_length; status = zephir_call_user_function(NULL, NULL, zephir_fcall_function, func, rvp, cache_entry, cache_slot, param_count, params, &info TSRMLS_CC); #else ALLOC_INIT_ZVAL(func); ZVAL_STRINGL(func, func_name, func_length, 0); status = zephir_call_user_function(NULL, NULL, zephir_fcall_function, func, rvp, cache_entry, 0, param_count, params, NULL TSRMLS_CC); #endif if (status == FAILURE && !EG(exception)) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined function %s()", func_name); if (return_value_ptr) { *return_value_ptr = NULL; } } else { if (EG(exception)) { status = FAILURE; if (return_value_ptr) { *return_value_ptr = NULL; } } } if (rv) { zval_ptr_dtor(&rv); } #if PHP_VERSION_ID < 50600 if (Z_REFCOUNT_P(func) > 1) { zval_copy_ctor(func); } else { ZVAL_NULL(func); } zval_ptr_dtor(&func); #endif return status; } int zephir_call_zval_func_aparams(zval **return_value_ptr, zval *func_name, zephir_fcall_cache_entry **cache_entry, int cache_slot, uint param_count, zval **params TSRMLS_DC) { int status; zval *rv = NULL, **rvp = return_value_ptr ? return_value_ptr : &rv; #ifndef ZEPHIR_RELEASE if (return_value_ptr && *return_value_ptr) { fprintf(stderr, "%s: *return_value_ptr must be NULL\n", __func__); zephir_print_backtrace(); abort(); } #endif status = zephir_call_user_function(NULL, NULL, zephir_fcall_function, func_name, rvp, cache_entry, cache_slot, param_count, params, NULL TSRMLS_CC); if (status == FAILURE && !EG(exception)) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined function %s()", Z_TYPE_P(func_name) ? Z_STRVAL_P(func_name) : "undefined"); if (return_value_ptr) { *return_value_ptr = NULL; } } else { if (EG(exception)) { status = FAILURE; if (return_value_ptr) { *return_value_ptr = NULL; } } } if (rv) { zval_ptr_dtor(&rv); } return status; } int zephir_call_class_method_aparams(zval **return_value_ptr, zend_class_entry *ce, zephir_call_type type, zval *object, const char *method_name, uint method_len, zephir_fcall_cache_entry **cache_entry, int cache_slot, uint param_count, zval **params TSRMLS_DC) { char *possible_method; zval *rv = NULL, **rvp = return_value_ptr ? return_value_ptr : &rv; zval *fn = NULL; #if PHP_VERSION_ID < 50600 zval *mn; #endif int status; #if PHP_VERSION_ID >= 50600 zephir_fcall_info info; #endif #ifndef ZEPHIR_RELEASE if (return_value_ptr && *return_value_ptr) { fprintf(stderr, "%s: *return_value_ptr must be NULL\n", __func__); zephir_print_backtrace(); abort(); } #endif if (object) { if (Z_TYPE_P(object) != IS_OBJECT) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Trying to call method %s on a non-object", method_name); if (return_value_ptr) { *return_value_ptr = NULL; } return FAILURE; } } #if PHP_VERSION_ID >= 50600 if (!cache_entry || !*cache_entry) { switch (type) { case zephir_fcall_parent: info.type = ZEPHIR_FCALL_TYPE_CLASS_PARENT_METHOD; break; case zephir_fcall_self: assert(!ce); info.type = ZEPHIR_FCALL_TYPE_CLASS_SELF_METHOD; break; case zephir_fcall_static: assert(!ce); info.type = ZEPHIR_FCALL_TYPE_CLASS_STATIC_METHOD; break; case zephir_fcall_ce: assert(ce != NULL); info.type = ZEPHIR_FCALL_TYPE_CE_METHOD; info.ce = ce; break; case zephir_fcall_method: default: assert(object != NULL); info.type = ZEPHIR_FCALL_TYPE_ZVAL_METHOD; info.object_ptr = object; info.ce = ce; break; } info.func_name = method_name; info.func_length = method_len; } status = zephir_call_user_function(object ? &object : NULL, ce, type, fn, rvp, cache_entry, cache_slot, param_count, params, &info TSRMLS_CC); #else ALLOC_INIT_ZVAL(fn); if (!cache_entry || !*cache_entry) { array_init_size(fn, 2); switch (type) { case zephir_fcall_parent: add_next_index_stringl(fn, ZEND_STRL("parent"), 1); break; case zephir_fcall_self: assert(!ce); add_next_index_stringl(fn, ZEND_STRL("self"), 1); break; case zephir_fcall_static: assert(!ce); add_next_index_stringl(fn, ZEND_STRL("static"), 1); break; case zephir_fcall_ce: assert(ce != NULL); add_next_index_stringl(fn, ce->name, ce->name_length, 1); break; case zephir_fcall_method: default: assert(object != NULL); Z_ADDREF_P(object); add_next_index_zval(fn, object); break; } ALLOC_INIT_ZVAL(mn); ZVAL_STRINGL(mn, method_name, method_len, 1); add_next_index_zval(fn, mn); } else { ZVAL_STRINGL(fn, "undefined", sizeof("undefined")-1, 1); } status = zephir_call_user_function(object ? &object : NULL, ce, type, fn, rvp, cache_entry, cache_slot, param_count, params, NULL TSRMLS_CC); #endif if (status == FAILURE && !EG(exception)) { if (ce) { possible_method = zephir_fcall_possible_method(ce, method_name TSRMLS_CC); } else { possible_method = "undefined"; } switch (type) { case zephir_fcall_parent: if (possible_method) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method parent::%s(), did you mean '%s'?", method_name, possible_method); } else { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method parent::%s()", method_name); } break; case zephir_fcall_self: if (possible_method) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method self::%s(), did you mean '%s'?", method_name, possible_method); } else { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method self::%s()", method_name); } break; case zephir_fcall_static: zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method static::%s()", method_name); break; case zephir_fcall_ce: zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method %s::%s()", ce->name, method_name); break; case zephir_fcall_method: if (possible_method) { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method %s::%s(), did you mean '%s'?", ce->name, method_name, possible_method); } else { zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method %s::%s()", ce->name, method_name); } break; default: zephir_throw_exception_format(spl_ce_RuntimeException TSRMLS_CC, "Call to undefined method ?::%s()", method_name); } if (return_value_ptr) { *return_value_ptr = NULL; } } else { if (EG(exception)) { status = FAILURE; if (return_value_ptr) { *return_value_ptr = NULL; } } } if (rv) { zval_ptr_dtor(&rv); } #if PHP_VERSION_ID < 50600 zval_ptr_dtor(&fn); #endif return status; } /** * Replaces call_user_func_array avoiding function lookup * This function does not return FAILURE if an exception has ocurred */ int zephir_call_user_func_array_noex(zval *return_value, zval *handler, zval *params TSRMLS_DC){ zval *retval_ptr = NULL; zend_fcall_info fci; zend_fcall_info_cache fci_cache; char *is_callable_error = NULL; int status = FAILURE; if (params && Z_TYPE_P(params) != IS_ARRAY) { ZVAL_NULL(return_value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid arguments supplied for zephir_call_user_func_array_noex()"); return FAILURE; } if (zend_fcall_info_init(handler, 0, &fci, &fci_cache, NULL, &is_callable_error TSRMLS_CC) == SUCCESS) { if (is_callable_error) { zend_error(E_STRICT, "%s", is_callable_error); efree(is_callable_error); } status = SUCCESS; } else { if (is_callable_error) { zend_error(E_WARNING, "%s", is_callable_error); efree(is_callable_error); } else { status = SUCCESS; } } if (status == SUCCESS) { zend_fcall_info_args(&fci, params TSRMLS_CC); fci.retval_ptr_ptr = &retval_ptr; if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && fci.retval_ptr_ptr && *fci.retval_ptr_ptr) { COPY_PZVAL_TO_ZVAL(*return_value, *fci.retval_ptr_ptr); } if (fci.params) { efree(fci.params); } } if (EG(exception)) { status = SUCCESS; } return status; } #if PHP_VERSION_ID <= 50309 /** * Latest version of zend_throw_exception_internal */ static void zephir_throw_exception_internal(zval *exception TSRMLS_DC) { if (exception != NULL) { zval *previous = EG(exception); zend_exception_set_previous(exception, EG(exception) TSRMLS_CC); EG(exception) = exception; if (previous) { return; } } if (!EG(current_execute_data)) { if (EG(exception)) { zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } zend_error(E_ERROR, "Exception thrown without a stack frame"); } if (zend_throw_exception_hook) { zend_throw_exception_hook(exception TSRMLS_CC); } if (EG(current_execute_data)->opline == NULL || (EG(current_execute_data)->opline + 1)->opcode == ZEND_HANDLE_EXCEPTION) { /* no need to rethrow the exception */ return; } EG(opline_before_exception) = EG(current_execute_data)->opline; EG(current_execute_data)->opline = EG(exception_op); } int zephir_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TSRMLS_DC) { zend_uint i; zval **original_return_value; HashTable *calling_symbol_table; zend_op_array *original_op_array; zend_op **original_opline_ptr; zend_class_entry *current_scope; zend_class_entry *current_called_scope; zend_class_entry *calling_scope = NULL; zend_class_entry *called_scope = NULL; zval *current_this; zend_execute_data execute_data; *fci->retval_ptr_ptr = NULL; if (!EG(active)) { return FAILURE; /* executor is already inactive */ } if (EG(exception)) { return FAILURE; /* we would result in an instable executor otherwise */ } switch (fci->size) { case sizeof(zend_fcall_info): break; /* nothing to do currently */ default: zend_error(E_ERROR, "Corrupted fcall_info provided to zephir_call_function()"); break; } /* Initialize execute_data */ if (EG(current_execute_data)) { execute_data = *EG(current_execute_data); EX(op_array) = NULL; EX(opline) = NULL; EX(object) = NULL; } else { /* This only happens when we're called outside any execute()'s * It shouldn't be strictly necessary to NULL execute_data out, * but it may make bugs easier to spot */ memset(&execute_data, 0, sizeof(zend_execute_data)); } if (!fci_cache || !fci_cache->initialized) { zend_fcall_info_cache fci_cache_local; char *callable_name; char *error = NULL; if (!fci_cache) { fci_cache = &fci_cache_local; } if (!zend_is_callable_ex(fci->function_name, fci->object_ptr, IS_CALLABLE_CHECK_SILENT, &callable_name, NULL, fci_cache, &error TSRMLS_CC)) { if (error) { zend_error(E_WARNING, "Invalid callback %s, %s", callable_name, error); efree(error); } if (callable_name) { efree(callable_name); } return FAILURE; } else { if (error) { /* Capitalize the first latter of the error message */ if (error[0] >= 'a' && error[0] <= 'z') { error[0] += ('A' - 'a'); } zend_error(E_STRICT, "%s", error); efree(error); } } efree(callable_name); } EX(function_state).function = fci_cache->function_handler; calling_scope = fci_cache->calling_scope; called_scope = fci_cache->called_scope; fci->object_ptr = fci_cache->object_ptr; EX(object) = fci->object_ptr; if (fci->object_ptr && Z_TYPE_P(fci->object_ptr) == IS_OBJECT && (!EG(objects_store).object_buckets || !EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(fci->object_ptr)].valid)) { return FAILURE; } #ifndef ZEPHIR_RELEASE if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) { zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", EX(function_state).function->common.scope->name, EX(function_state).function->common.function_name); return FAILURE; } #endif ZEND_VM_STACK_GROW_IF_NEEDED(fci->param_count + 1); for (i = 0; i < fci->param_count; i++) { zval *param; if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION && (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 && !ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i + 1) && PZVAL_IS_REF(*fci->params[i])) { ALLOC_ZVAL(param); *param = **(fci->params[i]); INIT_PZVAL(param); zval_copy_ctor(param); } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i + 1) && !PZVAL_IS_REF(*fci->params[i])) { if (Z_REFCOUNT_PP(fci->params[i]) > 1) { zval *new_zval; if (fci->no_separation && !ARG_MAY_BE_SENT_BY_REF(EX(function_state).function, i + 1)) { if (i || UNEXPECTED(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) == EG(argument_stack)->top)) { /* hack to clean up the stack */ zend_vm_stack_push_nocheck((void *) (zend_uintptr_t)i TSRMLS_CC); #if PHP_VERSION_ID <= 50500 zend_vm_stack_clear_multiple(TSRMLS_C); #else zend_vm_stack_clear_multiple(0 TSRMLS_C); #endif } zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given", i+1, EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name : "", EX(function_state).function->common.scope ? "::" : "", EX(function_state).function->common.function_name); return FAILURE; } ALLOC_ZVAL(new_zval); *new_zval = **fci->params[i]; zval_copy_ctor(new_zval); Z_SET_REFCOUNT_P(new_zval, 1); Z_DELREF_PP(fci->params[i]); *fci->params[i] = new_zval; } Z_ADDREF_PP(fci->params[i]); Z_SET_ISREF_PP(fci->params[i]); param = *fci->params[i]; } else if (*fci->params[i] != &EG(uninitialized_zval)) { Z_ADDREF_PP(fci->params[i]); param = *fci->params[i]; } else { ALLOC_ZVAL(param); *param = **(fci->params[i]); INIT_PZVAL(param); } zend_vm_stack_push_nocheck(param TSRMLS_CC); } EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C); zend_vm_stack_push_nocheck((void*)(zend_uintptr_t)fci->param_count TSRMLS_CC); current_scope = EG(scope); EG(scope) = calling_scope; current_this = EG(This); current_called_scope = EG(called_scope); if (called_scope) { EG(called_scope) = called_scope; } else { if (EX(function_state).function->type != ZEND_INTERNAL_FUNCTION) { EG(called_scope) = NULL; } } if (fci->object_ptr) { if ((EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)) { EG(This) = NULL; } else { EG(This) = fci->object_ptr; if (!PZVAL_IS_REF(EG(This))) { Z_ADDREF_P(EG(This)); /* For $this pointer */ } else { zval *this_ptr; ALLOC_ZVAL(this_ptr); *this_ptr = *EG(This); INIT_PZVAL(this_ptr); zval_copy_ctor(this_ptr); EG(This) = this_ptr; } } } else { EG(This) = NULL; } EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = &execute_data; if (EX(function_state).function->type == ZEND_USER_FUNCTION) { calling_symbol_table = EG(active_symbol_table); EG(scope) = EX(function_state).function->common.scope; if (fci->symbol_table) { EG(active_symbol_table) = fci->symbol_table; } else { EG(active_symbol_table) = NULL; } original_return_value = EG(return_value_ptr_ptr); original_op_array = EG(active_op_array); EG(return_value_ptr_ptr) = fci->retval_ptr_ptr; EG(active_op_array) = (zend_op_array *) EX(function_state).function; original_opline_ptr = EG(opline_ptr); zend_execute(EG(active_op_array) TSRMLS_CC); if (!fci->symbol_table && EG(active_symbol_table)) { if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { zend_hash_destroy(EG(active_symbol_table)); FREE_HASHTABLE(EG(active_symbol_table)); } else { /* clean before putting into the cache, since clean could call dtors, which could use cached hash */ zend_hash_clean(EG(active_symbol_table)); *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); } } EG(active_symbol_table) = calling_symbol_table; EG(active_op_array) = original_op_array; EG(return_value_ptr_ptr)=original_return_value; EG(opline_ptr) = original_opline_ptr; } else if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { int call_via_handler = (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); if (EX(function_state).function->common.scope) { EG(scope) = EX(function_state).function->common.scope; } ((zend_internal_function *) EX(function_state).function)->handler(fci->param_count, *fci->retval_ptr_ptr, fci->retval_ptr_ptr, fci->object_ptr, 1 TSRMLS_CC); /* We shouldn't fix bad extensions here, because it can break proper ones (Bug #34045) if (!EX(function_state).function->common.return_reference) { INIT_PZVAL(*fci->retval_ptr_ptr); }*/ if (EG(exception) && fci->retval_ptr_ptr) { zval_ptr_dtor(fci->retval_ptr_ptr); *fci->retval_ptr_ptr = NULL; } if (call_via_handler) { /* We must re-initialize function again */ fci_cache->initialized = 0; } } else { ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); if (fci->object_ptr) { Z_OBJ_HT_P(fci->object_ptr)->call_method(EX(function_state).function->common.function_name, fci->param_count, *fci->retval_ptr_ptr, fci->retval_ptr_ptr, fci->object_ptr, 1 TSRMLS_CC); } else { zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); return FAILURE; } if (EX(function_state).function->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { efree(EX(function_state).function->common.function_name); } efree(EX(function_state).function); if (EG(exception) && fci->retval_ptr_ptr) { zval_ptr_dtor(fci->retval_ptr_ptr); *fci->retval_ptr_ptr = NULL; } } #if PHP_VERSION_ID <= 50500 zend_vm_stack_clear_multiple(TSRMLS_C); #else zend_vm_stack_clear_multiple(0 TSRMLS_C); #endif if (EG(This)) { zval_ptr_dtor(&EG(This)); } EG(called_scope) = current_called_scope; EG(scope) = current_scope; EG(This) = current_this; EG(current_execute_data) = EX(prev_execute_data); if (EG(exception)) { zephir_throw_exception_internal(NULL TSRMLS_CC); } return SUCCESS; } #endif void zephir_eval_php(zval *str, zval *retval_ptr, char *context TSRMLS_DC) { zend_eval_string_ex(Z_STRVAL_P(str), retval_ptr, context, 1 TSRMLS_CC); }