summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2017-06-25 17:55:56 +0200
committerNikita Popov <nikita.ppv@gmail.com>2017-06-25 18:26:03 +0200
commitee8e75aab4eeb88dde05b98fa982bbee745819b2 (patch)
tree675598d2ea0d874fb9f161a62206ac98b60d7715
parent205807f60ed3e76b318bbd398e51b764c7e2317d (diff)
downloadphp-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.c144
-rw-r--r--Zend/zend_API.h2
-rw-r--r--Zend/zend_hash.h27
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;