diff options
author | Ferenc Kovacs <tyra3l@gmail.com> | 2013-12-06 05:39:49 +0100 |
---|---|---|
committer | Ferenc Kovacs <tyra3l@gmail.com> | 2013-12-06 05:39:49 +0100 |
commit | 562fa252ad932f321131ab6f65ffaef8d62fc73a (patch) | |
tree | 75db37b349886e52699c390fdb0638ae9b7026ed | |
parent | f0248bdce598a382899056d348d204c9d61746df (diff) | |
parent | 611da37617749c81ab22b1e44a0cc1f294cc493a (diff) | |
download | php-git-562fa252ad932f321131ab6f65ffaef8d62fc73a.tar.gz |
Merge branch 'use-function' of git://github.com/igorw/php-src into igorw-use-function
38 files changed, 791 insertions, 26 deletions
diff --git a/Zend/tests/use_const/alias.phpt b/Zend/tests/use_const/alias.phpt new file mode 100644 index 0000000000..f179393006 --- /dev/null +++ b/Zend/tests/use_const/alias.phpt @@ -0,0 +1,26 @@ +--TEST-- +aliasing imported constants to resolve naming conflicts +--FILE-- +<?php + +namespace foo { + const baz = 42; +} + +namespace bar { + const baz = 43; +} + +namespace { + use const foo\baz as foo_baz, + bar\baz as bar_baz; + var_dump(foo_baz); + var_dump(bar_baz); + echo "Done\n"; +} + +?> +--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 0000000000..6eaed7f27d --- /dev/null +++ b/Zend/tests/use_const/basic.phpt @@ -0,0 +1,22 @@ +--TEST-- +import namespaced constant +--FILE-- +<?php + +namespace foo\bar { + const baz = 42; + const qux = 43; +} + +namespace { + use const foo\bar\baz, foo\bar\qux; + var_dump(baz); + var_dump(qux); + echo "Done\n"; +} + +?> +--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 0000000000..1977daa93b --- /dev/null +++ b/Zend/tests/use_const/case_sensivity.phpt @@ -0,0 +1,12 @@ +--TEST-- +importing const with same name but different case +--FILE-- +<?php + +namespace { + use const foo\bar; + use const foo\BAR; +} + +?> +--EXPECT-- diff --git a/Zend/tests/use_const/conflicting_use.phpt b/Zend/tests/use_const/conflicting_use.phpt new file mode 100644 index 0000000000..3b3c4b3262 --- /dev/null +++ b/Zend/tests/use_const/conflicting_use.phpt @@ -0,0 +1,21 @@ +--TEST-- +use const statements with conflicting names +--FILE-- +<?php + +namespace foo { + const baz = 42; +} + +namespace bar { + const baz = 42; +} + +namespace { + use const foo\baz, bar\baz; + echo "Done\n"; +} + +?> +--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 0000000000..8b563a4ca9 --- /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-- +<?php + +namespace { + const foo = 'foo'; +} + +namespace x { + use foo as bar; + use const foo as bar; + var_dump(bar); +} + +?> +--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 0000000000..5eb44be64a --- /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-- +<?php + +namespace { + use const foo\bar; + + const bar = 42; +} + +?> +--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 0000000000..f674ce81e8 --- /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-- +<?php + +namespace { + const bar = 42; + + use const foo\bar; +} + +namespace { + echo "Done"; +} + +?> +--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 0000000000..90ed451f36 --- /dev/null +++ b/Zend/tests/use_const/includes/foo_bar.php @@ -0,0 +1,5 @@ +<?php + +namespace foo; + +const bar = 'local bar'; diff --git a/Zend/tests/use_const/includes/foo_php_version.php b/Zend/tests/use_const/includes/foo_php_version.php new file mode 100644 index 0000000000..08f9fd150e --- /dev/null +++ b/Zend/tests/use_const/includes/foo_php_version.php @@ -0,0 +1,5 @@ +<?php + +namespace foo; + +const PHP_VERSION = 42; diff --git a/Zend/tests/use_const/includes/global_bar.php b/Zend/tests/use_const/includes/global_bar.php new file mode 100644 index 0000000000..609d17b7b5 --- /dev/null +++ b/Zend/tests/use_const/includes/global_bar.php @@ -0,0 +1,3 @@ +<?php + +const bar = 'global bar'; diff --git a/Zend/tests/use_const/includes/global_baz.php b/Zend/tests/use_const/includes/global_baz.php new file mode 100644 index 0000000000..8b6fba97b3 --- /dev/null +++ b/Zend/tests/use_const/includes/global_baz.php @@ -0,0 +1,3 @@ +<?php + +const baz = NULL; diff --git a/Zend/tests/use_const/no_global_fallback.phpt b/Zend/tests/use_const/no_global_fallback.phpt new file mode 100644 index 0000000000..a128f353ed --- /dev/null +++ b/Zend/tests/use_const/no_global_fallback.phpt @@ -0,0 +1,14 @@ +--TEST-- +non-existent imported constants should not be looked up in the global table +--FILE-- +<?php + +require 'includes/global_baz.php'; + +use const foo\bar\baz; +var_dump(baz); + +?> +--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 0000000000..b71f2ecc81 --- /dev/null +++ b/Zend/tests/use_const/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use const statement +--FILE-- +<?php + +namespace { + use const self as foo; + use const parent as bar; +} + +?> +--EXPECT-- diff --git a/Zend/tests/use_const/shadow_core.phpt b/Zend/tests/use_const/shadow_core.phpt new file mode 100644 index 0000000000..7d8bcbd189 --- /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-- +<?php + +require 'includes/foo_php_version.php'; + +use const foo\PHP_VERSION; + +var_dump(PHP_VERSION); +echo "Done\n"; + +?> +--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 0000000000..930cc9f0b8 --- /dev/null +++ b/Zend/tests/use_const/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global constant with a local version +--FILE-- +<?php + +namespace { + require 'includes/global_bar.php'; + require 'includes/foo_bar.php'; +} + +namespace { + var_dump(bar); +} + +namespace { + use const foo\bar; + var_dump(bar); + echo "Done\n"; +} + +?> +--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 0000000000..5f7e97fff8 --- /dev/null +++ b/Zend/tests/use_function/alias.phpt @@ -0,0 +1,30 @@ +--TEST-- +aliasing imported functions to resolve naming conflicts +--FILE-- +<?php + +namespace foo { + function baz() { + return 'foo.baz'; + } +} + +namespace bar { + function baz() { + return 'bar.baz'; + } +} + +namespace { + use function foo\baz as foo_baz, + bar\baz as bar_baz; + var_dump(foo_baz()); + var_dump(bar_baz()); + echo "Done\n"; +} + +?> +--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 0000000000..513a96620c --- /dev/null +++ b/Zend/tests/use_function/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +import namespaced function +--FILE-- +<?php + +namespace foo\bar { + function baz() { + return 'foo.bar.baz'; + } + function qux() { + return baz(); + } +} + +namespace { + use function foo\bar\baz, foo\bar\qux; + var_dump(baz()); + var_dump(qux()); + echo "Done\n"; +} + +?> +--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 0000000000..ba6e3a7e4b --- /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-- +<?php + +namespace { + use function foo\bar; + use function foo\BAR; +} + +?> +--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 0000000000..ccfb96103a --- /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-- +<?php + +if (0) { + function foo() { + } +} + +use function bar\foo; + +echo "Done"; + +?> +--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 0000000000..0221fbdebb --- /dev/null +++ b/Zend/tests/use_function/conflicting_use.phpt @@ -0,0 +1,25 @@ +--TEST-- +use function statements with conflicting names +--FILE-- +<?php + +namespace foo { + function baz() { + return 'foo.baz'; + } +} + +namespace bar { + function baz() { + return 'bar.baz'; + } +} + +namespace { + use function foo\baz, bar\baz; + echo "Done\n"; +} + +?> +--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 0000000000..2870512014 --- /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-- +<?php + +namespace { + function foo() { + return 'foo'; + } +} + +namespace x { + use foo as bar; + use function foo as bar; + var_dump(bar()); +} + +?> +--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 0000000000..2e0faf0da2 --- /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-- +<?php + +namespace { + const foo = 'foo.const'; + function foo() { + return 'foo.function'; + } +} + +namespace x { + use const foo as bar; + use function foo as bar; + var_dump(bar); + var_dump(bar()); +} + +?> +--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 0000000000..c542a4d549 --- /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-- +<?php + +namespace { + use function foo\bar; + + function bar() {} +} + +?> +--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 0000000000..91974e0783 --- /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-- +<?php + +namespace { + function bar() {} + + use function foo\bar; +} + +namespace { + echo "Done"; +} + +?> +--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 0000000000..c50ff7357a --- /dev/null +++ b/Zend/tests/use_function/ignore_constants.phpt @@ -0,0 +1,23 @@ +--TEST-- +use function should ignore namespaced constants +--FILE-- +<?php + +namespace foo { + const bar = 42; +} + +namespace { + const bar = 43; +} + +namespace { + use function foo\bar; + var_dump(bar); + echo "Done\n"; +} + +?> +--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 0000000000..6d2f8cab45 --- /dev/null +++ b/Zend/tests/use_function/includes/foo_bar.php @@ -0,0 +1,7 @@ +<?php + +namespace foo; + +function bar() { + return 'local bar'; +} diff --git a/Zend/tests/use_function/includes/foo_strlen.php b/Zend/tests/use_function/includes/foo_strlen.php new file mode 100644 index 0000000000..d2df2aa2b4 --- /dev/null +++ b/Zend/tests/use_function/includes/foo_strlen.php @@ -0,0 +1,7 @@ +<?php + +namespace foo; + +function strlen($str) { + return 4; +} diff --git a/Zend/tests/use_function/includes/global_bar.php b/Zend/tests/use_function/includes/global_bar.php new file mode 100644 index 0000000000..6d7d91f805 --- /dev/null +++ b/Zend/tests/use_function/includes/global_bar.php @@ -0,0 +1,5 @@ +<?php + +function bar() { + return 'global bar'; +} diff --git a/Zend/tests/use_function/includes/global_baz.php b/Zend/tests/use_function/includes/global_baz.php new file mode 100644 index 0000000000..6383b9dd38 --- /dev/null +++ b/Zend/tests/use_function/includes/global_baz.php @@ -0,0 +1,4 @@ +<?php + +function baz() { +} diff --git a/Zend/tests/use_function/no_global_fallback.phpt b/Zend/tests/use_function/no_global_fallback.phpt new file mode 100644 index 0000000000..6597d0d301 --- /dev/null +++ b/Zend/tests/use_function/no_global_fallback.phpt @@ -0,0 +1,13 @@ +--TEST-- +non-existent imported functions should not be looked up in the global table +--FILE-- +<?php + +require 'includes/global_baz.php'; + +use function foo\bar\baz; +var_dump(baz()); + +?> +--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 0000000000..5d012c10e5 --- /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-- +<?php + +namespace { + function test() { + echo "NO!"; + } +} +namespace foo { + use function bar\test; + test(); +} + +?> +--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 0000000000..f1e1fa84f1 --- /dev/null +++ b/Zend/tests/use_function/self_parent.phpt @@ -0,0 +1,12 @@ +--TEST-- +Allow self and parent in use function statement +--FILE-- +<?php + +namespace { + use function self as foo; + use function parent as bar; +} + +?> +--EXPECT-- diff --git a/Zend/tests/use_function/shadow_core.phpt b/Zend/tests/use_function/shadow_core.phpt new file mode 100644 index 0000000000..8f92ff1e1b --- /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-- +<?php + +require 'includes/foo_strlen.php'; + +use function foo\strlen; + +var_dump(strlen('foo bar baz')); +echo "Done\n"; + +?> +--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 0000000000..791bcdf4d5 --- /dev/null +++ b/Zend/tests/use_function/shadow_global.phpt @@ -0,0 +1,25 @@ +--TEST-- +shadowing a global function with a local version +--FILE-- +<?php + +namespace { + require 'includes/global_bar.php'; + require 'includes/foo_bar.php'; +} + +namespace { + var_dump(bar()); +} + +namespace { + use function foo\bar; + var_dump(bar()); + echo "Done\n"; +} + +?> +--EXPECT-- +string(10) "global bar" +string(9) "local bar" +Done diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0bfe1569b5..35f5f30cba 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 5ffd6b4855..e3f06a0012 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 b9a5b39914..27d471fa06 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 a71dfb0956..1eb7b3170b 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); } |