Commit: 5dd41627bed892e76492ae3945e3b0d9cac21019
Author: Igor Wiedler <i...@wiedler.ch> Tue, 16 Jul 2013 20:39:33
+0200
Parents: 1a88fcc2d818b8ccad8712e0566377b7ae4e13d1
Branches: PHP-5.6 master
Link:
http://git.php.net/?p=php-src.git;a=commitdiff;h=5dd41627bed892e76492ae3945e3b0d9cac21019
Log:
Add new 'use function' sequence for importing namespaced functions
This is specified as the use_function RFC:
* https://wiki.php.net/rfc/use_function
Changed paths:
A Zend/tests/use_function/alias.phpt
A Zend/tests/use_function/basic.phpt
A Zend/tests/use_function/conflicting_use.phpt
A Zend/tests/use_function/no_global_fallback.phpt
A Zend/tests/use_function/shadow_core.phpt
A Zend/tests/use_function/shadow_global.phpt
A Zend/tests/use_function/shadow_global_same_ns.phpt
M Zend/zend_compile.c
M Zend/zend_compile.h
M Zend/zend_globals.h
M Zend/zend_language_parser.y
diff --git a/Zend/tests/use_function/alias.phpt
b/Zend/tests/use_function/alias.phpt
new file mode 100644
index 0000000..5f7e97f
--- /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 0000000..513a966
--- /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/conflicting_use.phpt
b/Zend/tests/use_function/conflicting_use.phpt
new file mode 100644
index 0000000..ed531e4
--- /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 bar\baz as baz because the name is already in use in
%s on line %d
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 0000000..018ae92
--- /dev/null
+++ b/Zend/tests/use_function/no_global_fallback.phpt
@@ -0,0 +1,18 @@
+--TEST--
+non-existent imported functions should not be looked up in the global table
+--FILE--
+<?php
+
+namespace {
+ function baz() {
+ }
+}
+
+namespace {
+ 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/shadow_core.phpt
b/Zend/tests/use_function/shadow_core.phpt
new file mode 100644
index 0000000..b06fbfc
--- /dev/null
+++ b/Zend/tests/use_function/shadow_core.phpt
@@ -0,0 +1,26 @@
+--TEST--
+shadowing a global core function with a local version
+--FILE--
+<?php
+
+namespace foo {
+ function strlen($str) {
+ return 4;
+ }
+}
+
+namespace {
+ var_dump(strlen('foo bar baz'));
+}
+
+namespace {
+ use function foo\strlen;
+ var_dump(strlen('foo bar baz'));
+ echo "Done\n";
+}
+
+?>
+--EXPECT--
+int(11)
+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 0000000..b318299
--- /dev/null
+++ b/Zend/tests/use_function/shadow_global.phpt
@@ -0,0 +1,32 @@
+--TEST--
+shadowing a global function with a local version
+--FILE--
+<?php
+
+namespace {
+ function bar() {
+ return 'global bar';
+ }
+}
+
+namespace foo {
+ function bar() {
+ return 'local bar';
+ }
+}
+
+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/tests/use_function/shadow_global_same_ns.phpt
b/Zend/tests/use_function/shadow_global_same_ns.phpt
new file mode 100644
index 0000000..7a30c82
--- /dev/null
+++ b/Zend/tests/use_function/shadow_global_same_ns.phpt
@@ -0,0 +1,25 @@
+--TEST--
+shadowing global functions defined in the same namespace as use
+--FILE--
+<?php
+
+namespace foo {
+ function bar() {
+ return 'local';
+ }
+}
+
+namespace {
+ function bar() {
+ return 'global';
+ }
+
+ use function foo\bar;
+ var_dump(bar());
+ echo "Done\n";
+}
+
+?>
+--EXPECT--
+string(5) "local"
+Done
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index f250b2b..7696960 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -204,6 +204,7 @@ 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;
init_compiler_declarables(TSRMLS_C);
zend_stack_init(&CG(context_stack));
@@ -2094,6 +2095,20 @@ void zend_resolve_non_class_name(znode *element_name,
zend_bool check_namespace
return;
}
+ if (CG(current_import_function)) {
+ len = Z_STRLEN(element_name->u.constant)+1;
+ lcname =
zend_str_tolower_dup(Z_STRVAL(element_name->u.constant), len);
+ /* Check if function matches imported name */
+ if (zend_hash_find(CG(current_import_function), lcname, len,
(void**)&ns) == SUCCESS) {
+ zval_dtor(&element_name->u.constant);
+ element_name->u.constant = **ns;
+ zval_copy_ctor(&element_name->u.constant);
+ efree(lcname);
+ return;
+ }
+ efree(lcname);
+ }
+
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);
@@ -6997,6 +7012,12 @@ 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(doc_comment)) {
efree(CG(doc_comment));
CG(doc_comment) = NULL;
@@ -7089,6 +7110,57 @@ void zend_do_use(znode *ns_name, znode *new_name, int
is_global TSRMLS_DC) /* {{
}
/* }}} */
+void zend_do_use_function(znode *ns_name, znode *new_name, int is_global
TSRMLS_DC) /* {{{ */
+{
+ char *lcname;
+ zval *name, *ns, tmp;
+ zend_bool warn = 0;
+
+ 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);
+ }
+
+ ALLOC_ZVAL(ns);
+ *ns = ns_name->u.constant;
+ 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 {
+ *name = *ns;
+ zval_copy_ctor(name);
+ warn = !is_global && !CG(current_namespace);
+ }
+ }
+
+ lcname = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name));
+
+ if (((Z_STRLEN_P(name) == sizeof("self")-1) &&
+ !memcmp(lcname, "self", sizeof("self")-1)) ||
+ ((Z_STRLEN_P(name) == sizeof("parent")-1) &&
+ !memcmp(lcname, "parent", sizeof("parent")-1))) {
+ zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because '%s'
is a special class name", Z_STRVAL_P(ns), Z_STRVAL_P(name), Z_STRVAL_P(name));
+ }
+
+ if (zend_hash_add(CG(current_import_function), lcname,
Z_STRLEN_P(name)+1, &ns, sizeof(zval*), NULL) != SUCCESS) {
+ zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because the
name is already in use", Z_STRVAL_P(ns), Z_STRVAL_P(name));
+ }
+ if (warn) {
+ zend_error(E_WARNING, "The use statement with non-compound name
'%s' has no effect", Z_STRVAL_P(name));
+ }
+ efree(lcname);
+ zval_dtor(name);
+}
+/* }}} */
+
void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */
{
zend_op *opline;
@@ -7141,6 +7213,11 @@ 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;
+ }
}
/* }}} */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 9c55b5e..f2078ed 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -636,6 +636,7 @@ 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_function(znode *name, znode *new_name, int is_global
TSRMLS_DC);
void zend_do_end_compilation(TSRMLS_D);
void zend_do_resolve_class_name(znode *result, znode *class_name, int
is_static TSRMLS_DC);
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index b9a5b39..2a1194a 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -131,6 +131,7 @@ struct _zend_compiler_globals {
zval *current_namespace;
HashTable *current_import;
+ HashTable *current_import_function;
zend_bool in_namespace;
zend_bool has_bracketed_namespaces;
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 6a9a24a..df3e55f 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -240,6 +240,7 @@ 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); }
| constant_declaration ';' {
zend_verify_namespace(TSRMLS_C); }
;
@@ -255,6 +256,18 @@ 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); }
+;
+
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); }
--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php