From: Dmitry Stogov Date: Fri, 20 Jan 2017 07:39:27 +0000 (+0300) Subject: Optimized extract(). Avoided double hash lookups and repeatable checks through loop... X-Git-Tag: php-7.2.0alpha1~497 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1f7bf2bfd63d94ddf4e28f903c692850232ef798;p=php Optimized extract(). Avoided double hash lookups and repeatable checks through loop splitting. --- diff --git a/ext/standard/array.c b/ext/standard/array.c index c7ae08bf68..41a168f68e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1736,142 +1736,464 @@ PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t } /* }}} */ -/* {{{ proto int extract(array var_array [, int extract_type [, string prefix]]) - Imports variables into symbol table from an array */ -PHP_FUNCTION(extract) +static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */ { - zval *var_array_param, *prefix = NULL; - zend_long extract_type = EXTR_OVERWRITE; - zval *entry; - zend_string *var_name; - zend_ulong num_key; - int var_exists, count = 0; - int extract_refs = 0; int exception_thrown = 0; - zend_array *symbol_table; - zval var_array; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ARRAY(var_array_param) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(extract_type) - Z_PARAM_ZVAL_EX(prefix, 0, 1) - ZEND_PARSE_PARAMETERS_END(); + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; - extract_refs = (extract_type & EXTR_REFS); - if (extract_refs) { - SEPARATE_ZVAL(var_array_param); - } - extract_type &= 0xff; + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + continue; + } + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + } + } ZEND_HASH_FOREACH_END(); - if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) { - php_error_docref(NULL, E_WARNING, "Invalid extract type"); - return; - } + return count; +} +/* }}} */ - if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) { - php_error_docref(NULL, E_WARNING, "specified extract type requires the prefix parameter"); - return; - } +static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; - if (prefix) { - convert_to_string(prefix); - if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) { - php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier"); - return; + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; } - } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + continue; + } + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + } + } ZEND_HASH_FOREACH_END(); - if (zend_forbid_dynamic_call("extract()") == FAILURE) { - return; - } + return count; +} +/* }}} */ - symbol_table = zend_rebuild_symbol_table(); +static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; - /* The array might be stored in a local variable that will be overwritten. To avoid losing the - * reference in that case we work on a copy. */ - ZVAL_COPY(&var_array, var_array_param); + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + if (ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { + continue; + } + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + } + count++; + } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(var_array), num_key, var_name, entry) { - zval final_name; + return count; +} +/* }}} */ - ZVAL_NULL(&final_name); - var_exists = 0; +static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; - if (var_name) { - var_exists = zend_hash_exists_ind(symbol_table, var_name); - } else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) { - zend_string *str = zend_long_to_str(num_key); - php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); - zend_string_release(str); - } else { + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } continue; } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + if (ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { + continue; + } + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + } + count++; + } ZEND_HASH_FOREACH_END(); - switch (extract_type) { - case EXTR_IF_EXISTS: - if (!var_exists) break; - /* break omitted intentionally */ + return count; +} +/* }}} */ - case EXTR_OVERWRITE: - /* GLOBALS protection */ - if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { - break; - } - ZVAL_STR_COPY(&final_name, var_name); - break; +static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var, final_name; - case EXTR_PREFIX_IF_EXISTS: - if (var_exists) { - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + continue; } - break; - - case EXTR_PREFIX_SAME: - if (!var_exists && ZSTR_LEN(var_name) != 0) { - ZVAL_STR_COPY(&final_name, var_name); + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; } - /* break omitted intentionally */ + } + zend_string_release(Z_STR(final_name)); + } + } ZEND_HASH_FOREACH_END(); - case EXTR_PREFIX_ALL: - if (Z_TYPE(final_name) == IS_NULL && ZSTR_LEN(var_name) != 0) { - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); - } - break; + return count; +} +/* }}} */ - case EXTR_PREFIX_INVALID: - if (Z_TYPE(final_name) == IS_NULL) { - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); +static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + continue; + } + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); } else { - ZVAL_STR_COPY(&final_name, var_name); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } - break; + } + zend_string_release(Z_STR(final_name)); + } + } ZEND_HASH_FOREACH_END(); - default: - if (!var_exists) { - ZVAL_STR_COPY(&final_name, var_name); + return count; +} +/* }}} */ + +static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (ZSTR_LEN(var_name) == 0) { + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + continue; } - break; + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; + } + } + zend_string_release(Z_STR(final_name)); + } else { + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + count++; } + } ZEND_HASH_FOREACH_END(); - if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { - zval *orig_var; + return count; +} +/* }}} */ - if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { +static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (ZSTR_LEN(var_name) == 0) { + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + continue; + } + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; + } + } + zend_string_release(Z_STR(final_name)); + } else { + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { if (!exception_thrown) { exception_thrown = 1; zend_throw_error(NULL, "Cannot re-assign $this"); } - zval_dtor(&final_name); continue; } - if (extract_refs) { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + count++; + } + } ZEND_HASH_FOREACH_END(); + return count; +} +/* }}} */ + +static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zend_ulong num_key; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) { + if (var_name) { + if (ZSTR_LEN(var_name) == 0) { + continue; + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + } else { + zend_string *str = zend_long_to_str(num_key); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); + zend_string_release(str); + } + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { ZVAL_MAKE_REF(entry); Z_ADDREF_P(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); @@ -1879,7 +2201,42 @@ PHP_FUNCTION(extract) zval_ptr_dtor(orig_var); ZVAL_COPY_VALUE(orig_var, entry); } else { - zend_hash_update(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; + } + } + zend_string_release(Z_STR(final_name)); + } ZEND_HASH_FOREACH_END(); + + return count; +} +/* }}} */ + +static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zend_ulong num_key; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) { + if (var_name) { + if (ZSTR_LEN(var_name) == 0) { + continue; + } + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + } else { + zend_string *str = zend_long_to_str(num_key); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); + zend_string_release(str); + } + if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); } } else { ZVAL_DEREF(entry); @@ -1892,14 +2249,311 @@ PHP_FUNCTION(extract) zval_ptr_dtor(orig_var); ZVAL_COPY_VALUE(orig_var, entry); } else { - zend_hash_update(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; + } + } + zend_string_release(Z_STR(final_name)); + } ZEND_HASH_FOREACH_END(); + + return count; +} +/* }}} */ + +static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zend_ulong num_key; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) { + if (var_name) { + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zend_string_release(Z_STR(final_name)); + continue; + } + } else { + ZVAL_STR_COPY(&final_name, var_name); + } + } else { + zend_string *str = zend_long_to_str(num_key); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); + zend_string_release(str); + if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zend_string_release(Z_STR(final_name)); + continue; + } + } + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + } + count++; + } + zend_string_release(Z_STR(final_name)); + } ZEND_HASH_FOREACH_END(); + + return count; +} +/* }}} */ + +static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zval *prefix) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zend_ulong num_key; + zval *entry, *orig_var, final_name; + + ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) { + if (var_name) { + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); + if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zend_string_release(Z_STR(final_name)); + continue; + } + } else { + ZVAL_STR_COPY(&final_name, var_name); + } + } else { + zend_string *str = zend_long_to_str(num_key); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); + zend_string_release(str); + if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zend_string_release(Z_STR(final_name)); + continue; + } + } + if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + } else { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } count++; } - zval_dtor(&final_name); + zend_string_release(Z_STR(final_name)); } ZEND_HASH_FOREACH_END(); - zval_ptr_dtor(&var_array); + + return count; +} +/* }}} */ + +static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; + + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + } + } + } else { + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + count++; + } + } ZEND_HASH_FOREACH_END(); + + return count; +} +/* }}} */ + +static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */ +{ + int exception_thrown = 0; + zend_long count = 0; + zend_string *var_name; + zval *entry, *orig_var; + + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) { + if (!var_name) { + continue; + } + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + continue; + } + if (ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + continue; + } + orig_var = zend_hash_find(symbol_table, var_name); + if (orig_var) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + if (Z_TYPE_P(orig_var) == IS_UNDEF) { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + ZVAL_COPY_VALUE(orig_var, entry); + count++; + } + } + } else { + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + zend_hash_add_new(symbol_table, var_name, entry); + count++; + } + } ZEND_HASH_FOREACH_END(); + + return count; +} +/* }}} */ + +/* {{{ proto int extract(array var_array [, int extract_type [, string prefix]]) + Imports variables into symbol table from an array */ +PHP_FUNCTION(extract) +{ + zval *var_array_param, *prefix = NULL; + zend_long extract_refs; + zend_long extract_type = EXTR_OVERWRITE; + zend_long count; + zend_array *symbol_table; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ARRAY(var_array_param) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(extract_type) + Z_PARAM_ZVAL_EX(prefix, 0, 1) + ZEND_PARSE_PARAMETERS_END(); + + extract_refs = (extract_type & EXTR_REFS); + if (extract_refs) { + SEPARATE_ZVAL(var_array_param); + } + extract_type &= 0xff; + + if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) { + php_error_docref(NULL, E_WARNING, "Invalid extract type"); + return; + } + + if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) { + php_error_docref(NULL, E_WARNING, "specified extract type requires the prefix parameter"); + return; + } + + if (prefix) { + convert_to_string(prefix); + if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) { + php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier"); + return; + } + } + + if (zend_forbid_dynamic_call("extract()") == FAILURE) { + return; + } + + symbol_table = zend_rebuild_symbol_table(); + + if (extract_refs) { + switch (extract_type) { + case EXTR_IF_EXISTS: + count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table); + break; + case EXTR_OVERWRITE: + count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table); + break; + case EXTR_PREFIX_IF_EXISTS: + count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_SAME: + count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_ALL: + count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_INVALID: + count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + default: + count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table); + break; + } + } else { + switch (extract_type) { + case EXTR_IF_EXISTS: + count = php_extract_if_exists(Z_ARRVAL_P(var_array_param), symbol_table); + break; + case EXTR_OVERWRITE: + count = php_extract_overwrite(Z_ARRVAL_P(var_array_param), symbol_table); + break; + case EXTR_PREFIX_IF_EXISTS: + count = php_extract_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_SAME: + count = php_extract_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_ALL: + count = php_extract_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + case EXTR_PREFIX_INVALID: + count = php_extract_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix); + break; + default: + count = php_extract_skip(Z_ARRVAL_P(var_array_param), symbol_table); + break; + } + } RETURN_LONG(count); }