📄 overload.c
字号:
/* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 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. | +----------------------------------------------------------------------+ | Author: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ *//* * TODO: * + provide a way for user to enable/disabling overloading of get/set/call * individually * - call original overloaded handlers if necessary * + use local copy of CE with NULL'ed out handler when calling object's * overloaded function * - handle both OE_IS_OBJECT and OE_IS_ARRAY in the whole chain * + see how to fix the issue of object trying to set its own property inside * the handler * + check if function exists in function table, then call it, otherwise * call handler (aka AUTOLOAD in Perl) * + should it check for existing properties first before calling __get/__set: * yes * + turn off all overloading handlers on a call to a handler * - pass array overloading info on to handlers? * - add unoverload() function */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "php.h"#include "php_ini.h"#include "ext/standard/info.h"#include "php_overload.h"#ifndef ZEND_ENGINE_2#if HAVE_OVERLOAD#define GET_HANDLER "__get"#define SET_HANDLER "__set"#define CALL_HANDLER "__call"#define DISABLE_HANDLERS(ce) \ (ce).handle_property_get = NULL; \ (ce).handle_property_set = NULL; \ (ce).handle_function_call = NULL;typedef struct _oo_class_data { void (*handle_function_call)(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference); zval (*handle_property_get)(zend_property_reference *property_reference); int (*handle_property_set)(zend_property_reference *property_reference, zval *value); HashTable getters; HashTable setters;} oo_class_data;ZEND_DECLARE_MODULE_GLOBALS(overload)function_entry overload_functions[] = { PHP_FE(overload, NULL) {NULL, NULL, NULL}};zend_module_entry overload_module_entry = { STANDARD_MODULE_HEADER, "overload", overload_functions, PHP_MINIT(overload), PHP_MSHUTDOWN(overload), NULL, PHP_RSHUTDOWN(overload), PHP_MINFO(overload), NO_VERSION_YET, STANDARD_MODULE_PROPERTIES};#ifdef COMPILE_DL_OVERLOADZEND_GET_MODULE(overload)#endifstatic void overloaded_class_dtor(oo_class_data *oo_data){ zend_hash_destroy(&oo_data->getters); zend_hash_destroy(&oo_data->setters);}/* {{{ php_overload_init_globals */static void php_overload_init_globals(zend_overload_globals *overload_globals TSRMLS_DC){ zend_hash_init(&overload_globals->overloaded_classes, 10, NULL, (dtor_func_t)overloaded_class_dtor, 1);}/* }}} *//* {{{ php_overload_destroy_globals */static void php_overload_destroy_globals(zend_overload_globals *overload_globals TSRMLS_DC){ zend_hash_destroy(&overload_globals->overloaded_classes);}/* }}} *//* {{{ PHP_MINIT_FUNCTION(overload) */PHP_MINIT_FUNCTION(overload){ ZEND_INIT_MODULE_GLOBALS(overload, php_overload_init_globals, php_overload_destroy_globals); /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ return SUCCESS;}/* }}} *//* {{{ PHP_MSHUTDOWN_FUNCTION(overload) */PHP_MSHUTDOWN_FUNCTION(overload){#ifdef ZTS ts_free_id(overload_globals_id);#else php_overload_destroy_globals(&overload_globals TSRMLS_CC);#endif return SUCCESS;}/* }}} *//* {{{ PHP_RSHUTDOWN_FUNCTION(overload) */PHP_RSHUTDOWN_FUNCTION(overload){ zend_hash_clean(&OOG(overloaded_classes)); return SUCCESS;}/* }}} *//* {{{ PHP_MINFO_FUNCTION */PHP_MINFO_FUNCTION(overload){ php_info_print_table_start(); php_info_print_table_row(2, "User-Space Object Overloading Support", "enabled"); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */}/* }}} *//* * In all three handlers, we save the original CE of the object, and replace it * with a temporary one that has all handlers turned off. This is to avoid * recursive calls to our handlers. We can't simply set a handler to NULL on the * original CE, as that would disable overloading on other objects of the same * class. After invoking the callback we restore the object's CE. *//* {{{ static int call_get_handler() */static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSRMLS_DC){ int call_result; zend_class_entry temp_ce, *orig_ce; zval result, *result_ptr = &result; zval get_handler; zval **args[2]; zval *retval = NULL; char *lcase_prop_name; zval **accessor_name; oo_class_data *oo_data; if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void**)&oo_data) == FAILURE) { php_error(E_WARNING, "internal problem trying to get property"); return 0; } temp_ce = *Z_OBJCE_P(object); DISABLE_HANDLERS(temp_ce); orig_ce = Z_OBJCE_P(object); Z_OBJ_P(object)->ce = &temp_ce; result_ptr->is_ref = 1; result_ptr->refcount = 1; ZVAL_NULL(result_ptr); lcase_prop_name = estrndup(Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name)); zend_str_tolower(lcase_prop_name, Z_STRLEN_P(prop_name)); if (zend_hash_find(&oo_data->getters, lcase_prop_name, Z_STRLEN_P(prop_name)+1, (void **)&accessor_name) == SUCCESS) { efree(lcase_prop_name); args[0] = &result_ptr; call_result = call_user_function_ex(NULL, &object, *accessor_name, &retval, 1, args, 0, NULL TSRMLS_CC); Z_OBJ_P(object)->ce = orig_ce; if (call_result == FAILURE || !retval) { php_error(E_WARNING, "unable to call %s::" GET_HANDLER "_%s() handler", Z_OBJCE_P(object)->name, Z_STRVAL_P(prop_name)); return 0; } } else { efree(lcase_prop_name); ZVAL_STRINGL(&get_handler, GET_HANDLER, sizeof(GET_HANDLER)-1, 0); args[0] = &prop_name; args[1] = &result_ptr; call_result = call_user_function_ex(NULL, &object, &get_handler, &retval, 2, args, 0, NULL TSRMLS_CC); Z_OBJ_P(object)->ce = orig_ce; if (call_result == FAILURE || !retval) { php_error(E_WARNING, "unable to call %s::" GET_HANDLER "() handler", Z_OBJCE_P(object)->name); return 0; } } if (zval_is_true(retval)) { REPLACE_ZVAL_VALUE(prop_value, result_ptr, 0); zval_ptr_dtor(&retval); return 1; } zval_ptr_dtor(&retval); zval_dtor(result_ptr); if (!oo_data->handle_property_get) { return 0; } /* TODO: call original OO handler */ return 0;}/* }}} *//* {{{ static int call_set_handler() */int call_set_handler(zval *object, zval *prop_name, zval *value TSRMLS_DC){ int call_result; zend_class_entry temp_ce, *orig_ce; zval set_handler; zval *value_copy; zval **args[2]; zval *retval = NULL; char *lcase_prop_name; zval **accessor_name; oo_class_data *oo_data; if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void**)&oo_data) == FAILURE) { php_error(E_WARNING, "internal problem trying to set property"); return 0; } temp_ce = *Z_OBJCE_P(object); DISABLE_HANDLERS(temp_ce); orig_ce = Z_OBJCE_P(object); Z_OBJ_P(object)->ce = &temp_ce; if (value->refcount == 0) { MAKE_STD_ZVAL(value_copy); *value_copy = *value; zval_copy_ctor(value_copy); value = value_copy; } lcase_prop_name = estrndup(Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name)); zend_str_tolower(lcase_prop_name, Z_STRLEN_P(prop_name)); if (zend_hash_find(&oo_data->setters, lcase_prop_name, Z_STRLEN_P(prop_name)+1, (void **)&accessor_name) == SUCCESS) { efree(lcase_prop_name); args[0] = &value; call_result = call_user_function_ex(NULL, &object, *accessor_name, &retval, 1, args, 0, NULL TSRMLS_CC); Z_OBJ_P(object)->ce = orig_ce; if (call_result == FAILURE || !retval) { php_error(E_WARNING, "unable to call %s::" SET_HANDLER "_%s() handler", Z_OBJCE_P(object)->name, Z_STRVAL_P(prop_name)); return 0; } } else { efree(lcase_prop_name); ZVAL_STRINGL(&set_handler, SET_HANDLER, sizeof(SET_HANDLER)-1, 0); args[0] = &prop_name; args[1] = &value; call_result = call_user_function_ex(NULL, &object, &set_handler, &retval, 2, args, 0, NULL TSRMLS_CC); Z_OBJ_P(object)->ce = orig_ce; if (call_result == FAILURE || !retval) { php_error(E_WARNING, "unable to call %s::" SET_HANDLER "() handler", orig_ce->name); return 0; } } if (zval_is_true(retval)) { zval_ptr_dtor(&retval); return 1; } zval_ptr_dtor(&retval); if (!oo_data->handle_property_set) { return 0; } /* TODO: call original OO handler */ return 0;}/* }}} */#define CLEANUP_OO_CHAIN() { \ for (; element; element=element->next) { \ zval_dtor(&((zend_overloaded_element *)element->data)->element); \ } \} \/* {{{ zval overload_get_property() */static zval overload_get_property(zend_property_reference *property_reference){ zval result; zval *result_ptr = &result; zend_overloaded_element *overloaded_property; zend_llist_element *element; zval object = *property_reference->object; zval **real_prop;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -