]> granicus.if.org Git - php/commitdiff
Extract zend_get_callable_name() API
authorNikita Popov <nikita.ppv@gmail.com>
Sun, 25 Jun 2017 15:55:56 +0000 (17:55 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Sun, 25 Jun 2017 16:26:03 +0000 (18:26 +0200)
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...

Zend/zend_API.c
Zend/zend_API.h
Zend/zend_hash.h

index fef56750a21cc8f5f2cfe38691b200ab7653f966..bf2619e8e2c27e8de727a13213da27e07be6c1e8 100644 (file)
@@ -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);
index df04be1fbe38a17c23097a5223c5f35cc656a848..4ce6d91b877f123bd74ac4165717426464683c16 100644 (file)
@@ -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);
index 6b54bda2c7cd8dfb212550a32b50e237a8f12e38..82e106530341a8b827b877be3310e43e0c72b587 100644 (file)
@@ -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;