diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2017-06-25 17:55:56 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2017-06-25 18:26:03 +0200 |
commit | ee8e75aab4eeb88dde05b98fa982bbee745819b2 (patch) | |
tree | 675598d2ea0d874fb9f161a62206ac98b60d7715 | |
parent | 205807f60ed3e76b318bbd398e51b764c7e2317d (diff) | |
download | php-git-ee8e75aab4eeb88dde05b98fa982bbee745819b2.tar.gz |
Extract zend_get_callable_name() API
Instead of interleaving this inside zend_is_callable(), implement
this in a separate function instead.
Also add _deref() hash APIs. I've wanted these for a while, and
this is another place where they're useful, so finally do it...
-rw-r--r-- | Zend/zend_API.c | 144 | ||||
-rw-r--r-- | Zend/zend_API.h | 2 | ||||
-rw-r--r-- | Zend/zend_hash.h | 27 |
3 files changed, 115 insertions, 58 deletions
diff --git a/Zend/zend_API.c b/Zend/zend_API.c index fef56750a2..bf2619e8e2 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3241,14 +3241,86 @@ get_function_via_handler: } /* }}} */ -ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */ +static zend_string *zend_create_method_string(zend_string *class_name, zend_string *method_name) { + zend_string *callable_name = zend_string_alloc( + ZSTR_LEN(class_name) + ZSTR_LEN(method_name) + sizeof("::") - 1, 0); + char *ptr = ZSTR_VAL(callable_name); + memcpy(ptr, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); + ptr += ZSTR_LEN(class_name); + memcpy(ptr, "::", sizeof("::") - 1); + ptr += sizeof("::") - 1; + memcpy(ptr, ZSTR_VAL(method_name), ZSTR_LEN(method_name) + 1); + return callable_name; +} + +ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object) /* {{{ */ +{ +try_again: + switch (Z_TYPE_P(callable)) { + case IS_STRING: + if (object) { + return zend_create_method_string(object->ce->name, Z_STR_P(callable)); + } + return zend_string_copy(Z_STR_P(callable)); + + case IS_ARRAY: + { + zval *method = NULL; + zval *obj = NULL; + + if (zend_hash_num_elements(Z_ARRVAL_P(callable)) == 2) { + obj = zend_hash_index_find_deref(Z_ARRVAL_P(callable), 0); + method = zend_hash_index_find_deref(Z_ARRVAL_P(callable), 1); + } + + if (obj == NULL || method == NULL || Z_TYPE_P(method) != IS_STRING) { + return zend_string_init("Array", sizeof("Array")-1, 0); + } + + if (Z_TYPE_P(obj) == IS_STRING) { + return zend_create_method_string(Z_STR_P(obj), Z_STR_P(method)); + } else if (Z_TYPE_P(obj) == IS_OBJECT) { + return zend_create_method_string(Z_OBJCE_P(obj)->name, Z_STR_P(method)); + } else { + return zend_string_init("Array", sizeof("Array")-1, 0); + } + } + case IS_OBJECT: + { + zend_class_entry *calling_scope; + zend_function *fptr; + zend_object *object; + if (Z_OBJ_HANDLER_P(callable, get_closure) + && Z_OBJ_HANDLER_P(callable, get_closure)(callable, &calling_scope, &fptr, &object) == SUCCESS) { + zend_class_entry *ce = Z_OBJCE_P(callable); + zend_string *callable_name = zend_string_alloc( + ZSTR_LEN(ce->name) + sizeof("::__invoke") - 1, 0); + memcpy(ZSTR_VAL(callable_name), ZSTR_VAL(ce->name), ZSTR_LEN(ce->name)); + memcpy(ZSTR_VAL(callable_name) + ZSTR_LEN(ce->name), "::__invoke", sizeof("::__invoke")); + return callable_name; + } + return zval_get_string(callable); + } + case IS_REFERENCE: + callable = Z_REFVAL_P(callable); + goto try_again; + default: + return zval_get_string(callable); + } +} +/* }}} */ + +ZEND_API zend_string *zend_get_callable_name(zval *callable) /* {{{ */ +{ + return zend_get_callable_name_ex(callable, NULL); +} +/* }}} */ + +static zend_bool zend_is_callable_impl(zval *callable, zend_object *object, uint32_t check_flags, zend_fcall_info_cache *fcc, char **error) /* {{{ */ { zend_bool ret; zend_fcall_info_cache fcc_local; - if (callable_name) { - *callable_name = NULL; - } if (fcc == NULL) { fcc = &fcc_local; } @@ -3274,20 +3346,8 @@ again: if (object) { fcc->object = object; fcc->calling_scope = object->ce; - if (callable_name) { - char *ptr; - - *callable_name = zend_string_alloc(ZSTR_LEN(fcc->calling_scope->name) + Z_STRLEN_P(callable) + sizeof("::") - 1, 0); - ptr = ZSTR_VAL(*callable_name); - memcpy(ptr, ZSTR_VAL(fcc->calling_scope->name), ZSTR_LEN(fcc->calling_scope->name)); - ptr += ZSTR_LEN(fcc->calling_scope->name); - memcpy(ptr, "::", sizeof("::") - 1); - ptr += sizeof("::") - 1; - memcpy(ptr, Z_STRVAL_P(callable), Z_STRLEN_P(callable) + 1); - } - } else if (callable_name) { - *callable_name = zend_string_copy(Z_STR_P(callable)); } + if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { fcc->called_scope = fcc->calling_scope; return 1; @@ -3329,19 +3389,6 @@ again: ZVAL_DEREF(obj); if (Z_TYPE_P(obj) == IS_STRING) { - if (callable_name) { - char *ptr; - - - *callable_name = zend_string_alloc(Z_STRLEN_P(obj) + Z_STRLEN_P(method) + sizeof("::") - 1, 0); - ptr = ZSTR_VAL(*callable_name); - memcpy(ptr, Z_STRVAL_P(obj), Z_STRLEN_P(obj)); - ptr += Z_STRLEN_P(obj); - memcpy(ptr, "::", sizeof("::") - 1); - ptr += sizeof("::") - 1; - memcpy(ptr, Z_STRVAL_P(method), Z_STRLEN_P(method) + 1); - } - if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { return 1; } @@ -3360,18 +3407,6 @@ again: fcc->object = Z_OBJ_P(obj); - if (callable_name) { - char *ptr; - - *callable_name = zend_string_alloc(ZSTR_LEN(fcc->calling_scope->name) + Z_STRLEN_P(method) + sizeof("::") - 1, 0); - ptr = ZSTR_VAL(*callable_name); - memcpy(ptr, ZSTR_VAL(fcc->calling_scope->name), ZSTR_LEN(fcc->calling_scope->name)); - ptr += ZSTR_LEN(fcc->calling_scope->name); - memcpy(ptr, "::", sizeof("::") - 1); - ptr += sizeof("::") - 1; - memcpy(ptr, Z_STRVAL_P(method), Z_STRLEN_P(method) + 1); - } - if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { fcc->called_scope = fcc->calling_scope; return 1; @@ -3405,42 +3440,35 @@ again: } else { if (error) zend_spprintf(error, 0, "array must have exactly two members"); } - if (callable_name) { - *callable_name = zend_string_init("Array", sizeof("Array")-1, 0); - } } return 0; case IS_OBJECT: if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(callable, &fcc->calling_scope, &fcc->function_handler, &fcc->object) == SUCCESS) { fcc->called_scope = fcc->calling_scope; - if (callable_name) { - zend_class_entry *ce = Z_OBJCE_P(callable); /* TBFixed: what if it's overloaded? */ - - *callable_name = zend_string_alloc(ZSTR_LEN(ce->name) + sizeof("::__invoke") - 1, 0); - memcpy(ZSTR_VAL(*callable_name), ZSTR_VAL(ce->name), ZSTR_LEN(ce->name)); - memcpy(ZSTR_VAL(*callable_name) + ZSTR_LEN(ce->name), "::__invoke", sizeof("::__invoke")); - } fcc->initialized = 1; return 1; } - if (callable_name) { - *callable_name = zval_get_string(callable); - } if (error) zend_spprintf(error, 0, "no array or string given"); return 0; case IS_REFERENCE: callable = Z_REFVAL_P(callable); goto again; default: - if (callable_name) { - *callable_name = zval_get_string(callable); - } if (error) zend_spprintf(error, 0, "no array or string given"); return 0; } } /* }}} */ +ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */ +{ + zend_bool ret = zend_is_callable_impl(callable, object, check_flags, fcc, error); + if (callable_name) { + *callable_name = zend_get_callable_name_ex(callable, object); + } + return ret; +} + ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */ { return zend_is_callable_ex(callable, NULL, check_flags, callable_name, NULL, NULL); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index df04be1fbe..4ce6d91b87 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -323,6 +323,8 @@ ZEND_API ZEND_COLD void zend_wrong_param_count(void); #define IS_CALLABLE_STRICT (IS_CALLABLE_CHECK_IS_STATIC) +ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object); +ZEND_API zend_string *zend_get_callable_name(zval *callable); ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error); ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name); ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name); diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 6b54bda2c7..82e1065303 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -772,6 +772,33 @@ static zend_always_inline void *zend_hash_index_find_ptr(const HashTable *ht, ze } } +static zend_always_inline zval *zend_hash_index_find_deref(HashTable *ht, zend_ulong h) +{ + zval *zv = zend_hash_index_find(ht, h); + if (zv) { + ZVAL_DEREF(zv); + } + return zv; +} + +static zend_always_inline zval *zend_hash_find_deref(HashTable *ht, zend_string *str) +{ + zval *zv = zend_hash_find(ht, str); + if (zv) { + ZVAL_DEREF(zv); + } + return zv; +} + +static zend_always_inline zval *zend_hash_str_find_deref(HashTable *ht, const char *str, size_t len) +{ + zval *zv = zend_hash_str_find(ht, str, len); + if (zv) { + ZVAL_DEREF(zv); + } + return zv; +} + static zend_always_inline void *zend_symtable_str_find_ptr(HashTable *ht, const char *str, size_t len) { zend_ulong idx; |