diff --git a/Zend/tests/use_const/alias.phpt b/Zend/tests/use_const/alias.phpt new file mode 100644 index 00000000000..f179393006b --- /dev/null +++ b/Zend/tests/use_const/alias.phpt @@ -0,0 +1,26 @@ +--TEST-- +aliasing imported constants to resolve naming conflicts +--FILE-- + +--EXPECT-- +int(42) +int(43) +Done diff --git a/Zend/tests/use_const/basic.phpt b/Zend/tests/use_const/basic.phpt new file mode 100644 index 00000000000..6eaed7f27d3 --- /dev/null +++ b/Zend/tests/use_const/basic.phpt @@ -0,0 +1,22 @@ +--TEST-- +import namespaced constant +--FILE-- + +--EXPECT-- +int(42) +int(43) +Done diff --git a/Zend/tests/use_const/case_sensivity.phpt b/Zend/tests/use_const/case_sensivity.phpt new file mode 100644 index 00000000000..1977daa93bb --- /dev/null +++ b/Zend/tests/use_const/case_sensivity.phpt @@ -0,0 +1,12 @@ +--TEST-- +importing const with same name but different case +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_const/conflicting_use.phpt b/Zend/tests/use_const/conflicting_use.phpt new file mode 100644 index 00000000000..3b3c4b3262e --- /dev/null +++ b/Zend/tests/use_const/conflicting_use.phpt @@ -0,0 +1,21 @@ +--TEST-- +use const statements with conflicting names +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use const bar\baz as baz because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/conflicting_use_alias.phpt b/Zend/tests/use_const/conflicting_use_alias.phpt new file mode 100644 index 00000000000..8b563a4ca97 --- /dev/null +++ b/Zend/tests/use_const/conflicting_use_alias.phpt @@ -0,0 +1,18 @@ +--TEST-- +use and use const with the same alias +--FILE-- + +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/use_const/define_imported.phpt b/Zend/tests/use_const/define_imported.phpt new file mode 100644 index 00000000000..5eb44be64a0 --- /dev/null +++ b/Zend/tests/use_const/define_imported.phpt @@ -0,0 +1,14 @@ +--TEST-- +defining const with same name as imported should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare const bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/define_imported_before.phpt b/Zend/tests/use_const/define_imported_before.phpt new file mode 100644 index 00000000000..f674ce81e8e --- /dev/null +++ b/Zend/tests/use_const/define_imported_before.phpt @@ -0,0 +1,18 @@ +--TEST-- +using const with same name as defined should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use const foo\bar as bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_const/includes/foo_bar.php b/Zend/tests/use_const/includes/foo_bar.php new file mode 100644 index 00000000000..90ed451f364 --- /dev/null +++ b/Zend/tests/use_const/includes/foo_bar.php @@ -0,0 +1,5 @@ + +--EXPECTF-- +Notice: Use of undefined constant baz - assumed 'baz' in %s on line %d +string(3) "baz" diff --git a/Zend/tests/use_const/self_parent.phpt b/Zend/tests/use_const/self_parent.phpt new file mode 100644 index 00000000000..b71f2ecc81f --- /dev/null +++ b/Zend/tests/use_const/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use const statement +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_const/shadow_core.phpt b/Zend/tests/use_const/shadow_core.phpt new file mode 100644 index 00000000000..7d8bcbd1892 --- /dev/null +++ b/Zend/tests/use_const/shadow_core.phpt @@ -0,0 +1,16 @@ +--TEST-- +shadowing a global core constant with a local version +--FILE-- + +--EXPECTF-- +int(42) +Done diff --git a/Zend/tests/use_const/shadow_global.phpt b/Zend/tests/use_const/shadow_global.phpt new file mode 100644 index 00000000000..930cc9f0b87 --- /dev/null +++ b/Zend/tests/use_const/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global constant with a local version +--FILE-- + +--EXPECT-- +string(10) "global bar" +string(9) "local bar" +Done diff --git a/Zend/tests/use_function/alias.phpt b/Zend/tests/use_function/alias.phpt new file mode 100644 index 00000000000..5f7e97fff87 --- /dev/null +++ b/Zend/tests/use_function/alias.phpt @@ -0,0 +1,30 @@ +--TEST-- +aliasing imported functions to resolve naming conflicts +--FILE-- + +--EXPECT-- +string(7) "foo.baz" +string(7) "bar.baz" +Done diff --git a/Zend/tests/use_function/basic.phpt b/Zend/tests/use_function/basic.phpt new file mode 100644 index 00000000000..513a96620c1 --- /dev/null +++ b/Zend/tests/use_function/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +import namespaced function +--FILE-- + +--EXPECT-- +string(11) "foo.bar.baz" +string(11) "foo.bar.baz" +Done diff --git a/Zend/tests/use_function/case_insensivity.phpt b/Zend/tests/use_function/case_insensivity.phpt new file mode 100644 index 00000000000..ba6e3a7e4b1 --- /dev/null +++ b/Zend/tests/use_function/case_insensivity.phpt @@ -0,0 +1,13 @@ +--TEST-- +importing function with same name but different case should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use function foo\BAR as BAR because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/conditional_function_declaration.phpt b/Zend/tests/use_function/conditional_function_declaration.phpt new file mode 100644 index 00000000000..ccfb96103a6 --- /dev/null +++ b/Zend/tests/use_function/conditional_function_declaration.phpt @@ -0,0 +1,17 @@ +--TEST-- +function that is conditionally defined at runtime should not cause compiler error +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/use_function/conflicting_use.phpt b/Zend/tests/use_function/conflicting_use.phpt new file mode 100644 index 00000000000..0221fbdebb3 --- /dev/null +++ b/Zend/tests/use_function/conflicting_use.phpt @@ -0,0 +1,25 @@ +--TEST-- +use function statements with conflicting names +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use function bar\baz as baz because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/conflicting_use_alias.phpt b/Zend/tests/use_function/conflicting_use_alias.phpt new file mode 100644 index 00000000000..2870512014d --- /dev/null +++ b/Zend/tests/use_function/conflicting_use_alias.phpt @@ -0,0 +1,20 @@ +--TEST-- +use and use function with the same alias +--FILE-- + +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/use_function/conflicting_use_const_alias.phpt b/Zend/tests/use_function/conflicting_use_const_alias.phpt new file mode 100644 index 00000000000..2e0faf0da21 --- /dev/null +++ b/Zend/tests/use_function/conflicting_use_const_alias.phpt @@ -0,0 +1,23 @@ +--TEST-- +use const and use function with the same alias +--FILE-- + +--EXPECT-- +string(9) "foo.const" +string(12) "foo.function" diff --git a/Zend/tests/use_function/define_imported.phpt b/Zend/tests/use_function/define_imported.phpt new file mode 100644 index 00000000000..c542a4d5494 --- /dev/null +++ b/Zend/tests/use_function/define_imported.phpt @@ -0,0 +1,14 @@ +--TEST-- +defining function with same name as imported should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare function bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/define_imported_before.phpt b/Zend/tests/use_function/define_imported_before.phpt new file mode 100644 index 00000000000..91974e0783d --- /dev/null +++ b/Zend/tests/use_function/define_imported_before.phpt @@ -0,0 +1,18 @@ +--TEST-- +using function with same name as defined should fail +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use function foo\bar as bar because the name is already in use in %s on line %d diff --git a/Zend/tests/use_function/ignore_constants.phpt b/Zend/tests/use_function/ignore_constants.phpt new file mode 100644 index 00000000000..c50ff7357af --- /dev/null +++ b/Zend/tests/use_function/ignore_constants.phpt @@ -0,0 +1,23 @@ +--TEST-- +use function should ignore namespaced constants +--FILE-- + +--EXPECT-- +int(43) +Done diff --git a/Zend/tests/use_function/includes/foo_bar.php b/Zend/tests/use_function/includes/foo_bar.php new file mode 100644 index 00000000000..6d2f8cab45f --- /dev/null +++ b/Zend/tests/use_function/includes/foo_bar.php @@ -0,0 +1,7 @@ + +--EXPECTF-- +Fatal error: Call to undefined function foo\bar\baz() in %s on line %d diff --git a/Zend/tests/use_function/no_global_fallback2.phpt b/Zend/tests/use_function/no_global_fallback2.phpt new file mode 100644 index 00000000000..5d012c10e59 --- /dev/null +++ b/Zend/tests/use_function/no_global_fallback2.phpt @@ -0,0 +1,18 @@ +--TEST-- +non-existent imported functions should not be looked up in the global table +--FILE-- + +--EXPECTF-- +Fatal error: Call to undefined function bar\test() in %s on line %d diff --git a/Zend/tests/use_function/self_parent.phpt b/Zend/tests/use_function/self_parent.phpt new file mode 100644 index 00000000000..f1e1fa84f1d --- /dev/null +++ b/Zend/tests/use_function/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use function statement +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/use_function/shadow_core.phpt b/Zend/tests/use_function/shadow_core.phpt new file mode 100644 index 00000000000..8f92ff1e1be --- /dev/null +++ b/Zend/tests/use_function/shadow_core.phpt @@ -0,0 +1,16 @@ +--TEST-- +shadowing a global core function with a local version +--FILE-- + +--EXPECT-- +int(4) +Done diff --git a/Zend/tests/use_function/shadow_global.phpt b/Zend/tests/use_function/shadow_global.phpt new file mode 100644 index 00000000000..791bcdf4d50 --- /dev/null +++ b/Zend/tests/use_function/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global function with a local version +--FILE-- + +--EXPECT-- +string(10) "global bar" +string(9) "local bar" +Done diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0bfe1569b5c..35f5f30cba8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -197,6 +197,9 @@ void zend_init_compiler_data_structures(TSRMLS_D) /* {{{ */ CG(in_namespace) = 0; CG(has_bracketed_namespaces) = 0; CG(current_import) = NULL; + CG(current_import_function) = NULL; + CG(current_import_const) = NULL; + zend_hash_init(&CG(const_filenames), 0, NULL, NULL, 0); init_compiler_declarables(TSRMLS_C); zend_stack_init(&CG(context_stack)); @@ -235,6 +238,7 @@ void shutdown_compiler(TSRMLS_D) /* {{{ */ zend_stack_destroy(&CG(list_stack)); zend_hash_destroy(&CG(filenames_table)); zend_llist_destroy(&CG(open_files)); + zend_hash_destroy(&CG(const_filenames)); zend_stack_destroy(&CG(context_stack)); } /* }}} */ @@ -417,12 +421,16 @@ int zend_add_ns_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); CALCULATE_LITERAL_HASH(lc_literal); - ns_separator = (const char*)zend_memrchr(Z_STRVAL_P(zv), '\\', Z_STRLEN_P(zv)) + 1; - lc_len = Z_STRLEN_P(zv) - (ns_separator - Z_STRVAL_P(zv)); - lc_name = zend_str_tolower_dup(ns_separator, lc_len); - ZVAL_STRINGL(&c, lc_name, lc_len, 0); - lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); - CALCULATE_LITERAL_HASH(lc_literal); + ns_separator = (const char*)zend_memrchr(Z_STRVAL_P(zv), '\\', Z_STRLEN_P(zv)); + + if (ns_separator != NULL) { + ns_separator += 1; + lc_len = Z_STRLEN_P(zv) - (ns_separator - Z_STRVAL_P(zv)); + lc_name = zend_str_tolower_dup(ns_separator, lc_len); + ZVAL_STRINGL(&c, lc_name, lc_len, 0); + lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + CALCULATE_LITERAL_HASH(lc_literal); + } return ret; } @@ -1684,6 +1692,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } else { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); zval key; + zval **ns_name; if (CG(current_namespace)) { /* Prefix function name with current namespace name */ @@ -1699,18 +1708,32 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n lcname = zend_str_tolower_dup(name, name_len); } + /* Function name must not conflict with import names */ + if (CG(current_import_function) && + zend_hash_find(CG(current_import_function), lcname, Z_STRLEN(function_name->u.constant)+1, (void**)&ns_name) == SUCCESS) { + + char *tmp = zend_str_tolower_dup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name)); + + if (Z_STRLEN_PP(ns_name) != Z_STRLEN(function_name->u.constant) || + memcmp(tmp, lcname, Z_STRLEN(function_name->u.constant))) { + zend_error(E_COMPILE_ERROR, "Cannot declare function %s because the name is already in use", Z_STRVAL(function_name->u.constant)); + } + efree(tmp); + } + opline->opcode = ZEND_DECLARE_FUNCTION; opline->op1_type = IS_CONST; build_runtime_defined_function_key(&key, lcname, name_len TSRMLS_CC); opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); Z_HASH_P(&CONSTANT(opline->op1.constant)) = zend_hash_func(Z_STRVAL(CONSTANT(opline->op1.constant)), Z_STRLEN(CONSTANT(opline->op1.constant))); opline->op2_type = IS_CONST; - LITERAL_STRINGL(opline->op2, lcname, name_len, 0); + LITERAL_STRINGL(opline->op2, lcname, name_len, 1); CALCULATE_LITERAL_HASH(opline->op2.constant); opline->extended_value = ZEND_DECLARE_FUNCTION; zend_hash_quick_update(CG(function_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); zend_stack_push(&CG(context_stack), (void *) &CG(context), sizeof(CG(context))); zend_init_compiler_context(TSRMLS_C); + str_efree(lcname); } if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) { @@ -1933,7 +1956,7 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace char *lcname; char *is_compound = memchr(Z_STRVAL(function_name->u.constant), '\\', Z_STRLEN(function_name->u.constant)); - zend_resolve_non_class_name(function_name, check_namespace TSRMLS_CC); + zend_resolve_function_name(function_name, &check_namespace TSRMLS_CC); if (check_namespace && CG(current_namespace) && !is_compound) { /* We assume we call function from the current namespace @@ -2070,12 +2093,12 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML } /* }}} */ -void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace TSRMLS_DC) /* {{{ */ +void zend_resolve_non_class_name(znode *element_name, zend_bool *check_namespace, zend_bool case_sensitive, HashTable *current_import_sub TSRMLS_DC) /* {{{ */ { znode tmp; int len; zval **ns; - char *lcname, *compound = memchr(Z_STRVAL(element_name->u.constant), '\\', Z_STRLEN(element_name->u.constant)); + char *lookup_name, *compound = memchr(Z_STRVAL(element_name->u.constant), '\\', Z_STRLEN(element_name->u.constant)); if (Z_STRVAL(element_name->u.constant)[0] == '\\') { /* name starts with \ so it is known and unambiguos, nothing to do here but shorten it */ @@ -2084,15 +2107,35 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace return; } - if(!check_namespace) { + if(!*check_namespace) { return; } + if (current_import_sub) { + len = Z_STRLEN(element_name->u.constant)+1; + if (case_sensitive) { + lookup_name = estrndup(Z_STRVAL(element_name->u.constant), len); + } else { + lookup_name = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); + } + /* Check if function/const matches imported name */ + if (zend_hash_find(current_import_sub, lookup_name, len, (void**)&ns) == SUCCESS) { + zval_dtor(&element_name->u.constant); + element_name->u.constant = **ns; + zval_copy_ctor(&element_name->u.constant); + efree(lookup_name); + *check_namespace = 0; + return; + } + efree(lookup_name); + } + if (compound && CG(current_import)) { len = compound - Z_STRVAL(element_name->u.constant); - lcname = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); + /* namespace is always lowercase */ + lookup_name = zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len); /* Check if first part of compound name is an import name */ - if (zend_hash_find(CG(current_import), lcname, len+1, (void**)&ns) == SUCCESS) { + if (zend_hash_find(CG(current_import), lookup_name, len+1, (void**)&ns) == SUCCESS) { /* Substitute import name */ tmp.op_type = IS_CONST; tmp.u.constant = **ns; @@ -2102,10 +2145,11 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace memmove(Z_STRVAL(element_name->u.constant), Z_STRVAL(element_name->u.constant)+len, Z_STRLEN(element_name->u.constant)+1); zend_do_build_namespace_name(&tmp, &tmp, element_name TSRMLS_CC); *element_name = tmp; - efree(lcname); + efree(lookup_name); + *check_namespace = 0; return; } - efree(lcname); + efree(lookup_name); } if (CG(current_namespace)) { @@ -2121,6 +2165,18 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace } /* }}} */ +void zend_resolve_function_name(znode *element_name, zend_bool *check_namespace TSRMLS_DC) /* {{{ */ +{ + zend_resolve_non_class_name(element_name, check_namespace, 0, CG(current_import_function) TSRMLS_CC); +} +/* }}} */ + +void zend_resolve_const_name(znode *element_name, zend_bool *check_namespace TSRMLS_DC) /* {{{ */ +{ + zend_resolve_non_class_name(element_name, check_namespace, 1, CG(current_import_const) TSRMLS_CC); +} +/* }}} */ + void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC) /* {{{ */ { char *lcname; @@ -5618,7 +5674,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con break; } - zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); + zend_resolve_const_name(constant_name, &check_namespace TSRMLS_CC); if(!compound) { fetch_type |= IS_CONSTANT_UNQUALIFIED; @@ -5630,7 +5686,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con case ZEND_RT: compound = memchr(Z_STRVAL(constant_name->u.constant), '\\', Z_STRLEN(constant_name->u.constant)); - zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); + zend_resolve_const_name(constant_name, &check_namespace TSRMLS_CC); if(zend_constant_ct_subst(result, &constant_name->u.constant, 1 TSRMLS_CC)) { break; @@ -6981,6 +7037,18 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC CG(current_import) = NULL; } + if (CG(current_import_function)) { + zend_hash_destroy(CG(current_import_function)); + efree(CG(current_import_function)); + CG(current_import_function) = NULL; + } + + if (CG(current_import_const)) { + zend_hash_destroy(CG(current_import_const)); + efree(CG(current_import_const)); + CG(current_import_const) = NULL; + } + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -7002,7 +7070,7 @@ void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{ } ALLOC_ZVAL(ns); - *ns = ns_name->u.constant; + ZVAL_ZVAL(ns, &ns_name->u.constant, 0, 0); if (new_name) { name = &new_name->u.constant; } else { @@ -7015,8 +7083,7 @@ void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{ if (p) { ZVAL_STRING(name, p+1, 1); } else { - *name = *ns; - zval_copy_ctor(name); + ZVAL_ZVAL(name, ns, 1, 0); warn = !is_global && !CG(current_namespace); } } @@ -7073,9 +7140,117 @@ void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{ } /* }}} */ +void zend_do_use_non_class(znode *ns_name, znode *new_name, int is_global, int is_function, zend_bool case_sensitive, HashTable *current_import_sub, HashTable *lookup_table TSRMLS_DC) /* {{{ */ +{ + char *lookup_name; + zval *name, *ns, tmp; + zend_bool warn = 0; + + ALLOC_ZVAL(ns); + ZVAL_ZVAL(ns, &ns_name->u.constant, 0, 0); + if (new_name) { + name = &new_name->u.constant; + } else { + const char *p; + + /* The form "use A\B" is eqivalent to "use A\B as B". + So we extract the last part of compound name to use as a new_name */ + name = &tmp; + p = zend_memrchr(Z_STRVAL_P(ns), '\\', Z_STRLEN_P(ns)); + if (p) { + ZVAL_STRING(name, p+1, 1); + } else { + ZVAL_ZVAL(name, ns, 1, 0); + warn = !is_global && !CG(current_namespace); + } + } + + if (case_sensitive) { + lookup_name = estrndup(Z_STRVAL_P(name), Z_STRLEN_P(name)); + } else { + lookup_name = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name)); + } + + if (CG(current_namespace)) { + /* Prefix import name with current namespace name to avoid conflicts with functions/consts */ + char *c_ns_name = emalloc(Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name) + 1); + + zend_str_tolower_copy(c_ns_name, Z_STRVAL_P(CG(current_namespace)), Z_STRLEN_P(CG(current_namespace))); + c_ns_name[Z_STRLEN_P(CG(current_namespace))] = '\\'; + memcpy(c_ns_name+Z_STRLEN_P(CG(current_namespace))+1, lookup_name, Z_STRLEN_P(name)+1); + if (zend_hash_exists(lookup_table, c_ns_name, Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name)+1)) { + char *tmp2 = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns)); + + if (Z_STRLEN_P(ns) != Z_STRLEN_P(CG(current_namespace)) + 1 + Z_STRLEN_P(name) || + memcmp(tmp2, c_ns_name, Z_STRLEN_P(ns))) { + zend_error(E_COMPILE_ERROR, "Cannot use %s %s as %s because the name is already in use", is_function ? "function" : "const", Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + efree(tmp2); + } + efree(c_ns_name); + } else if (is_function) { + zend_function *function; + + if (zend_hash_find(lookup_table, lookup_name, Z_STRLEN_P(name)+1, (void **) &function) == SUCCESS && function->type == ZEND_USER_FUNCTION && strcmp(function->op_array.filename, CG(compiled_filename)) == 0) { + char *c_tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns)); + + if (Z_STRLEN_P(ns) != Z_STRLEN_P(name) || + memcmp(c_tmp, lookup_name, Z_STRLEN_P(ns))) { + zend_error(E_COMPILE_ERROR, "Cannot use function %s as %s because the name is already in use", Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + efree(c_tmp); + } + } else { + const char *filename; + + if (zend_hash_find(lookup_table, lookup_name, Z_STRLEN_P(name)+1, (void **) &filename) == SUCCESS && strcmp(filename, CG(compiled_filename)) == 0) { + char *c_tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns)); + + if (Z_STRLEN_P(ns) != Z_STRLEN_P(name) || + memcmp(c_tmp, lookup_name, Z_STRLEN_P(ns))) { + zend_error(E_COMPILE_ERROR, "Cannot use const %s as %s because the name is already in use", Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + efree(c_tmp); + } + } + + if (zend_hash_add(current_import_sub, lookup_name, Z_STRLEN_P(name)+1, &ns, sizeof(zval*), NULL) != SUCCESS) { + zend_error(E_COMPILE_ERROR, "Cannot use %s %s as %s because the name is already in use", is_function ? "function" : "const", Z_STRVAL_P(ns), Z_STRVAL_P(name)); + } + if (warn) { + zend_error(E_WARNING, "The use %s statement with non-compound name '%s' has no effect", is_function ? "function" : "const", Z_STRVAL_P(name)); + } + efree(lookup_name); + zval_dtor(name); +} +/* }}} */ + +void zend_do_use_function(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{{ */ +{ + if (!CG(current_import_function)) { + CG(current_import_function) = emalloc(sizeof(HashTable)); + zend_hash_init(CG(current_import_function), 0, NULL, ZVAL_PTR_DTOR, 0); + } + + zend_do_use_non_class(ns_name, new_name, is_global, 1, 0, CG(current_import_function), CG(function_table) TSRMLS_CC); +} +/* }}} */ + +void zend_do_use_const(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{{ */ +{ + if (!CG(current_import_const)) { + CG(current_import_const) = emalloc(sizeof(HashTable)); + zend_hash_init(CG(current_import_const), 0, NULL, ZVAL_PTR_DTOR, 0); + } + + zend_do_use_non_class(ns_name, new_name, is_global, 0, 1, CG(current_import_const), &CG(const_filenames) TSRMLS_CC); +} +/* }}} */ + void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */ { zend_op *opline; + zval **ns_name; if(Z_TYPE(value->u.constant) == IS_CONSTANT_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Arrays are not allowed as constants"); @@ -7096,11 +7271,26 @@ void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */ *name = tmp; } + /* Constant name must not conflict with import names */ + if (CG(current_import_const) && + zend_hash_find(CG(current_import_const), Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant)+1, (void**)&ns_name) == SUCCESS) { + + char *tmp = estrndup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name)); + + if (Z_STRLEN_PP(ns_name) != Z_STRLEN(name->u.constant) || + memcmp(tmp, Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant))) { + zend_error(E_COMPILE_ERROR, "Cannot declare const %s because the name is already in use", Z_STRVAL(name->u.constant)); + } + efree(tmp); + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DECLARE_CONST; SET_UNUSED(opline->result); SET_NODE(opline->op1, name); SET_NODE(opline->op2, value); + + zend_hash_add(&CG(const_filenames), Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant)+1, CG(compiled_filename), strlen(CG(compiled_filename))+1, NULL); } /* }}} */ @@ -7125,6 +7315,16 @@ void zend_do_end_namespace(TSRMLS_D) /* {{{ */ efree(CG(current_import)); CG(current_import) = NULL; } + if (CG(current_import_function)) { + zend_hash_destroy(CG(current_import_function)); + efree(CG(current_import_function)); + CG(current_import_function) = NULL; + } + if (CG(current_import_const)) { + zend_hash_destroy(CG(current_import_const)); + efree(CG(current_import_const)); + CG(current_import_const) = NULL; + } } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 5ffd6b48553..e3f06a00127 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -75,7 +75,7 @@ typedef struct _zend_literal { #define Z_HASH_P(zv) \ (((zend_literal*)(zv))->hash_value) -typedef union _znode_op { +typedef union _znode_op { zend_uint constant; zend_uint var; zend_uint num; @@ -87,7 +87,7 @@ typedef union _znode_op { void *ptr; /* Used for passing pointers from the compile to execution phase, currently used for traits */ } znode_op; -typedef struct _znode { /* used only during compilation */ +typedef struct _znode { /* used only during compilation */ int op_type; union { znode_op op; @@ -244,7 +244,7 @@ typedef struct _zend_arg_info { } zend_arg_info; /* the following structure repeats the layout of zend_arg_info, - * but its fields have different meaning. It's used as the first element of + * but its fields have different meaning. It's used as the first element of * arg_info array to define properties of internal functions. */ typedef struct _zend_internal_function_info { @@ -267,7 +267,7 @@ typedef struct _zend_compiled_variable { struct _zend_op_array { /* Common elements */ zend_uchar type; - const char *function_name; + const char *function_name; zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; @@ -443,7 +443,9 @@ ZEND_API char *zend_get_compiled_filename(TSRMLS_D); ZEND_API int zend_get_compiled_lineno(TSRMLS_D); ZEND_API size_t zend_get_scanned_file_offset(TSRMLS_D); -void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace TSRMLS_DC); +void zend_resolve_non_class_name(znode *element_name, zend_bool *check_namespace, zend_bool case_sensitive, HashTable *current_import_sub TSRMLS_DC); +void zend_resolve_function_name(znode *element_name, zend_bool *check_namespace TSRMLS_DC); +void zend_resolve_const_name(znode *element_name, zend_bool *check_namespace TSRMLS_DC); void zend_resolve_class_name(znode *class_name TSRMLS_DC); ZEND_API const char* zend_get_compiled_variable_name(const zend_op_array *op_array, zend_uint var, int* name_len); @@ -642,6 +644,9 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_brackets TSRMLS_D void zend_do_end_namespace(TSRMLS_D); void zend_verify_namespace(TSRMLS_D); void zend_do_use(znode *name, znode *new_name, int is_global TSRMLS_DC); +void zend_do_use_non_class(znode *ns_name, znode *new_name, int is_global, int is_function, zend_bool case_sensitive, HashTable *current_import_sub, HashTable *lookup_table TSRMLS_DC); +void zend_do_use_function(znode *name, znode *new_name, int is_global TSRMLS_DC); +void zend_do_use_const(znode *name, znode *new_name, int is_global TSRMLS_DC); void zend_do_end_compilation(TSRMLS_D); void zend_do_constant_expression(znode *result, zend_ast *ast TSRMLS_DC); @@ -681,7 +686,7 @@ void zend_class_add_ref(zend_class_entry **ce); ZEND_API void zend_mangle_property_name(char **dest, int *dest_length, const char *src1, int src1_length, const char *src2, int src2_length, int internal); #define zend_unmangle_property_name(mangled_property, mangled_property_len, class_name, prop_name) \ - zend_unmangle_property_name_ex(mangled_property, mangled_property_len, class_name, prop_name, NULL) + zend_unmangle_property_name_ex(mangled_property, mangled_property_len, class_name, prop_name, NULL) ZEND_API int zend_unmangle_property_name_ex(const char *mangled_property, int mangled_property_len, const char **class_name, const char **prop_name, int *prop_len); #define ZEND_FUNCTION_DTOR (void (*)(void *)) zend_function_dtor diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index b9a5b39914a..27d471fa069 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -131,9 +131,13 @@ struct _zend_compiler_globals { zval *current_namespace; HashTable *current_import; + HashTable *current_import_function; + HashTable *current_import_const; zend_bool in_namespace; zend_bool has_bracketed_namespaces; + HashTable const_filenames; + zend_compiler_context context; zend_stack context_stack; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index a71dfb09560..1eb7b3170ba 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -241,6 +241,8 @@ top_statement: | T_NAMESPACE '{' { zend_do_begin_namespace(NULL, 1 TSRMLS_CC); } top_statement_list '}' { zend_do_end_namespace(TSRMLS_C); } | T_USE use_declarations ';' { zend_verify_namespace(TSRMLS_C); } + | T_USE T_FUNCTION use_function_declarations ';' { zend_verify_namespace(TSRMLS_C); } + | T_USE T_CONST use_const_declarations ';' { zend_verify_namespace(TSRMLS_C); } | constant_declaration ';' { zend_verify_namespace(TSRMLS_C); } ; @@ -256,6 +258,30 @@ use_declaration: | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use(&$2, &$4, 1 TSRMLS_CC); } ; +use_function_declarations: + use_function_declarations ',' use_function_declaration + | use_function_declaration +; + +use_function_declaration: + namespace_name { zend_do_use_function(&$1, NULL, 0 TSRMLS_CC); } + | namespace_name T_AS T_STRING { zend_do_use_function(&$1, &$3, 0 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name { zend_do_use_function(&$2, NULL, 1 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use_function(&$2, &$4, 1 TSRMLS_CC); } +; + +use_const_declarations: + use_const_declarations ',' use_const_declaration + | use_const_declaration +; + +use_const_declaration: + namespace_name { zend_do_use_const(&$1, NULL, 0 TSRMLS_CC); } + | namespace_name T_AS T_STRING { zend_do_use_const(&$1, &$3, 0 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name { zend_do_use_const(&$2, NULL, 1 TSRMLS_CC); } + | T_NS_SEPARATOR namespace_name T_AS T_STRING { zend_do_use_const(&$2, &$4, 1 TSRMLS_CC); } +; + constant_declaration: constant_declaration ',' T_STRING '=' static_scalar { zend_do_declare_constant(&$3, &$5 TSRMLS_CC); } | T_CONST T_STRING '=' static_scalar { zend_do_declare_constant(&$2, &$4 TSRMLS_CC); }