Hi,

I've just finished implementing use for functions. This patch is against PHP_5_3 and can be easily ported to HEAD. I am confident that it is quite mature and have tests in the patch to prove it.

the patch is also at http://pear.php.net/~greg/usefunctions.patch.txt

This patch implements the following syntax:

func.inc:
<?php
namespace foo;
function hi()
{
   echo "hi\n";
}
?>

main.php:
<?php
include 'func.inc';
use foo::hi, foo:hi as there;
foo::hi();
hi();
there();
?>

The output is "hi\nhi\nhi\n"

It can be used to alias any function, and so can also be useful for overriding an internal function and saving it:

<?php
namespace foo;
include 'another.inc';
use function::strlen as old_strlen, function::another::strlen;
?>

With this patch, function users should be a lot happier.

Thanks,
Greg
Index: Zend/zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.647.2.27.2.41.2.85
diff -u -u -r1.647.2.27.2.41.2.85 zend_compile.c
--- Zend/zend_compile.c 29 Aug 2008 10:17:08 -0000      1.647.2.27.2.41.2.85
+++ Zend/zend_compile.c 12 Sep 2008 01:56:14 -0000
@@ -139,6 +139,7 @@
        CG(start_lineno) = 0;
        CG(current_namespace) = NULL;
        CG(current_import) = NULL;
+       CG(current_import_functions) = NULL;
        init_compiler_declarables(TSRMLS_C);
        zend_hash_apply(CG(auto_globals), (apply_func_t) zend_auto_global_arm 
TSRMLS_CC);
        zend_stack_init(&CG(labels_stack));
@@ -1536,22 +1537,10 @@
        char *lcname;
        int prefix_len = 0;
 
-       if (check_namespace && CG(current_namespace)) {
-               /* We assume we call function from the current namespace
-                  if it is not prefixed. */
-               znode tmp;
-
-               tmp.op_type = IS_CONST;
-               tmp.u.constant = *CG(current_namespace);
-               zval_copy_ctor(&tmp.u.constant);
-               zend_do_build_namespace_name(&tmp, &tmp, function_name 
TSRMLS_CC);
-               *function_name = tmp;
-
-               /* In run-time PHP will check for function with full name and
-                  internal function with short name */
-               prefix_len = Z_STRLEN_P(CG(current_namespace)) + 2;
+       if (check_namespace) {
+               zend_resolve_function_name(function_name, &prefix_len 
TSRMLS_CC);
        }
-       
+
        lcname = zend_str_tolower_dup(function_name->u.constant.value.str.val, 
function_name->u.constant.value.str.len);
        if ((zend_hash_find(CG(function_table), lcname, 
function_name->u.constant.value.str.len+1, (void **) &function)==FAILURE) ||
                ((CG(compiler_options) & 
ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) &&
@@ -1672,6 +1661,55 @@
        zend_do_extended_fcall_begin(TSRMLS_C);
 }
 
+void zend_resolve_function_name(znode *function_name, int *prefix_len 
TSRMLS_DC)
+{
+       char *compound;
+       char *lcname;
+       zval **ns;
+       znode tmp;
+
+       compound = memchr(Z_STRVAL(function_name->u.constant), ':', 
Z_STRLEN(function_name->u.constant));
+       if (compound) {
+               /* This is a compound function name that contains namespace 
prefix */
+               if (Z_TYPE(function_name->u.constant) == IS_STRING &&
+                   Z_STRVAL(function_name->u.constant)[0] == ':') {
+                   /* The STRING name has "::" prefix */
+                   Z_STRLEN(function_name->u.constant) -= 2;
+                   memmove(Z_STRVAL(function_name->u.constant), 
Z_STRVAL(function_name->u.constant)+2, Z_STRLEN(function_name->u.constant)+1);
+                       Z_STRVAL(function_name->u.constant) = erealloc(
+                               Z_STRVAL(function_name->u.constant),
+                               Z_STRLEN(function_name->u.constant) + 1);
+
+                       /* check for self/parent/etc. */
+                       if (ZEND_FETCH_CLASS_DEFAULT != 
zend_get_class_fetch_type(Z_STRVAL(function_name->u.constant), 
Z_STRLEN(function_name->u.constant))) {
+                               zend_error(E_COMPILE_ERROR, "'::%s' is a wrong 
function name", Z_STRVAL(function_name->u.constant));
+                       }
+               }
+       } else if (CG(current_import_functions) || CG(current_namespace)) {
+               /* this is a plain name (without ::) */
+               lcname = 
zend_str_tolower_dup(Z_STRVAL(function_name->u.constant), 
Z_STRLEN(function_name->u.constant));
+
+               if (CG(current_import_functions) &&
+                   zend_hash_find(CG(current_import_functions), lcname, 
Z_STRLEN(function_name->u.constant)+1, (void**)&ns) == SUCCESS) {
+                   /* The given name is an import name. Substitute it. */
+                       zval_dtor(&function_name->u.constant);
+                       function_name->u.constant = **ns;
+                       zval_copy_ctor(&function_name->u.constant);
+               } else if (CG(current_namespace)) {
+                       tmp.op_type = IS_CONST;
+                       tmp.u.constant = *CG(current_namespace);
+                       zval_copy_ctor(&tmp.u.constant);
+                       zend_do_build_namespace_name(&tmp, &tmp, function_name 
TSRMLS_CC);
+                       *function_name = tmp;
+
+                       /* In run-time PHP will check for function with full 
name and
+                       internal function with short name */
+                       *prefix_len = Z_STRLEN_P(CG(current_namespace)) + 2;
+               }
+               efree(lcname);
+       }
+}
+
 void zend_resolve_class_name(znode *class_name, ulong *fetch_type, int 
check_ns_name TSRMLS_DC)
 {
        char *compound;
@@ -5075,16 +5113,26 @@
 }
 /* }}} */
 
-void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* 
{{{ */
+void zend_do_use(znode *ns_name, znode *new_name, int is_global, int 
is_function TSRMLS_DC) /* {{{ */
 {
        char *lcname;
        zval *name, *ns, tmp;
        zend_bool warn = 0;
        zend_class_entry **pce;
+       zend_function *func;
+       HashTable **import, *other;
 
-       if (!CG(current_import)) {
-               CG(current_import) = emalloc(sizeof(HashTable));
-               zend_hash_init(CG(current_import), 0, NULL, ZVAL_PTR_DTOR, 0);
+       if (is_function) {
+               import = &CG(current_import_functions);
+               other = CG(current_import);
+       } else {
+               import = &CG(current_import);
+               other = CG(current_import_functions);
+       }
+
+       if (!*import) {
+               *import = emalloc(sizeof(HashTable));
+               zend_hash_init(*import, 0, NULL, ZVAL_PTR_DTOR, 0);
        }
 
        ALLOC_ZVAL(ns);
@@ -5124,8 +5172,12 @@
                ns_name[Z_STRLEN_P(CG(current_namespace))] = ':';
                ns_name[Z_STRLEN_P(CG(current_namespace))+1] = ':';
                memcpy(ns_name+Z_STRLEN_P(CG(current_namespace))+2, lcname, 
Z_STRLEN_P(name)+1);
-               if (zend_hash_exists(CG(class_table), ns_name, 
Z_STRLEN_P(CG(current_namespace)) + 2 + Z_STRLEN_P(name)+1)) {
-                       char *tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), 
Z_STRLEN_P(ns));
+               if (is_function && zend_hash_exists(CG(function_table), 
ns_name, Z_STRLEN_P(CG(current_namespace)) + 2 + Z_STRLEN_P(name)+1)) {
+                       goto conflict_error;
+               } else if (!is_function && zend_hash_exists(CG(class_table), 
ns_name, Z_STRLEN_P(CG(current_namespace)) + 2 + Z_STRLEN_P(name)+1)) {
+                       char *tmp;
+conflict_error:
+                       tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), 
Z_STRLEN_P(ns));
 
                        if (Z_STRLEN_P(ns) != Z_STRLEN_P(CG(current_namespace)) 
+ 2 + Z_STRLEN_P(name) ||
                                memcmp(tmp, ns_name, Z_STRLEN_P(ns))) {
@@ -5134,10 +5186,16 @@
                        efree(tmp);
                }
                efree(ns_name);
-       } else if (zend_hash_find(CG(class_table), lcname, Z_STRLEN_P(name)+1, 
(void**)&pce) == SUCCESS &&
+       } else if (is_function && zend_hash_find(CG(function_table), lcname, 
Z_STRLEN_P(name)+1, (void**)&func) == SUCCESS &&
+                  func->common.type == ZEND_USER_FUNCTION &&
+                  func->op_array.filename == CG(compiled_filename)) {
+               goto use_in_use;
+       } else if (!is_function && zend_hash_find(CG(class_table), lcname, 
Z_STRLEN_P(name)+1, (void**)&pce) == SUCCESS &&
                   (*pce)->type == ZEND_USER_CLASS &&
                   (*pce)->filename == CG(compiled_filename)) {
-               char *tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), 
Z_STRLEN_P(ns));
+               char *tmp;
+use_in_use:
+               tmp = zend_str_tolower_dup(Z_STRVAL_P(ns), Z_STRLEN_P(ns));
 
                if (Z_STRLEN_P(ns) != Z_STRLEN_P(name) ||
                        memcmp(tmp, lcname, Z_STRLEN_P(ns))) {
@@ -5146,7 +5204,11 @@
                efree(tmp);
        }
 
-       if (zend_hash_add(CG(current_import), lcname, Z_STRLEN_P(name)+1, &ns, 
sizeof(zval*), NULL) != SUCCESS) {
+       if (other && zend_hash_exists(other, lcname, Z_STRLEN_P(name)+1)) {
+               goto use_in_use2;
+       }
+       if (zend_hash_add(*import, lcname, Z_STRLEN_P(name)+1, &ns, 
sizeof(zval*), NULL) != SUCCESS) {
+use_in_use2:
                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) {
@@ -5200,6 +5262,11 @@
                efree(CG(current_import));
                CG(current_import) = NULL;
        }
+       if (CG(current_import_functions)) {
+               zend_hash_destroy(CG(current_import_functions));
+               efree(CG(current_import_functions));
+               CG(current_import_functions) = NULL;
+       }
 }
 /* }}} */
 
Index: Zend/zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.316.2.8.2.12.2.33
diff -u -u -r1.316.2.8.2.12.2.33 zend_compile.h
--- Zend/zend_compile.h 29 Aug 2008 18:12:47 -0000      1.316.2.8.2.12.2.33
+++ Zend/zend_compile.h 12 Sep 2008 01:56:15 -0000
@@ -360,6 +360,7 @@
 ZEND_API size_t zend_get_scanned_file_offset(TSRMLS_D);
 
 void zend_resolve_class_name(znode *class_name, ulong *fetch_type, int 
check_ns_name TSRMLS_DC);
+void zend_resolve_function_name(znode *function_name, int *prefix_len 
TSRMLS_DC);
 ZEND_API char* zend_get_compiled_variable_name(const zend_op_array *op_array, 
zend_uint var, int* name_len);
 
 #ifdef ZTS
@@ -537,7 +538,7 @@
 void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC);
 void zend_do_build_namespace_name(znode *result, znode *prefix, znode *name 
TSRMLS_DC);
 void zend_do_namespace(const znode *name TSRMLS_DC);
-void zend_do_use(znode *name, znode *new_name, int is_global TSRMLS_DC);
+void zend_do_use(znode *name, znode *new_name, int is_global, int is_function 
TSRMLS_DC);
 void zend_do_end_compilation(TSRMLS_D);
 
 void zend_do_label(znode *label TSRMLS_DC);
Index: Zend/zend_globals.h
===================================================================
RCS file: /repository/ZendEngine2/zend_globals.h,v
retrieving revision 1.141.2.3.2.7.2.19
diff -u -u -r1.141.2.3.2.7.2.19 zend_globals.h
--- Zend/zend_globals.h 14 Aug 2008 10:24:51 -0000      1.141.2.3.2.7.2.19
+++ Zend/zend_globals.h 12 Sep 2008 01:56:18 -0000
@@ -134,6 +134,7 @@
 
        zval      *current_namespace;
        HashTable *current_import;
+       HashTable *current_import_functions;
 
        HashTable *labels;
        zend_stack labels_stack;
Index: Zend/zend_language_parser.y
===================================================================
RCS file: /repository/ZendEngine2/zend_language_parser.y,v
retrieving revision 1.160.2.4.2.8.2.26
diff -u -u -r1.160.2.4.2.8.2.26 zend_language_parser.y
--- Zend/zend_language_parser.y 29 Aug 2008 17:54:29 -0000      
1.160.2.4.2.8.2.26
+++ Zend/zend_language_parser.y 12 Sep 2008 01:56:23 -0000
@@ -164,6 +164,11 @@
        |       namespace_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { 
zend_do_build_namespace_name(&$$, &$1, &$3 TSRMLS_CC); }
 ;
 
+compound_namespace_name:
+               T_STRING T_PAAMAYIM_NEKUDOTAYIM T_STRING { 
zend_do_build_namespace_name(&$$, &$1, &$3 TSRMLS_CC); }
+       |       compound_namespace_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { 
zend_do_build_namespace_name(&$$, &$1, &$3 TSRMLS_CC); }
+;
+
 top_statement:
                statement
        |       function_declaration_statement  { 
zend_do_early_binding(TSRMLS_C); }
@@ -180,10 +185,14 @@
 ;
 
 use_declaration:
-               namespace_name                  { zend_do_use(&$1, NULL, 0 
TSRMLS_CC); }
-       |       namespace_name T_AS T_STRING    { zend_do_use(&$1, &$3, 0 
TSRMLS_CC); }
-       |       T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_use(&$2, NULL, 1 
TSRMLS_CC); }
-       |       T_PAAMAYIM_NEKUDOTAYIM T_STRING T_AS T_STRING { 
zend_do_use(&$2, &$4, 1 TSRMLS_CC); }
+               namespace_name                  { zend_do_use(&$1, NULL, 0, 0 
TSRMLS_CC); }
+       |       T_FUNCTION T_PAAMAYIM_NEKUDOTAYIM compound_namespace_name       
{ zend_do_use(&$3, NULL, 0, 1 TSRMLS_CC); }
+       |       namespace_name T_AS T_STRING    { zend_do_use(&$1, &$3, 0, 0 
TSRMLS_CC); }
+       |       T_FUNCTION T_PAAMAYIM_NEKUDOTAYIM compound_namespace_name T_AS 
T_STRING { zend_do_use(&$3, &$5, 0, 1 TSRMLS_CC); }
+       |       T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_use(&$2, NULL, 1, 0 
TSRMLS_CC); }
+       |       T_FUNCTION T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_use(&$3, 
NULL, 1, 1 TSRMLS_CC); }
+       |       T_PAAMAYIM_NEKUDOTAYIM T_STRING T_AS T_STRING { 
zend_do_use(&$2, &$4, 1, 0 TSRMLS_CC); }
+       |       T_FUNCTION T_PAAMAYIM_NEKUDOTAYIM T_STRING T_AS T_STRING { 
zend_do_use(&$3, &$5, 1, 1 TSRMLS_CC); }
 ;
 
 constant_declaration:
Index: Zend/tests/ns_072.phpt
===================================================================
RCS file: Zend/tests/ns_072.phpt
diff -N Zend/tests/ns_072.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_072.phpt      12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,19 @@
+--TEST--
+072: use for namespace functions
+--FILE--
+<?php
+namespace one;
+function hi()
+{
+echo "hi\n";
+}
+namespace two;
+use function::one::hi, function::one::hi as wow;
+hi();
+wow();
+?>
+===DONE===
+--EXPECT--
+hi
+hi
+===DONE===
Index: Zend/tests/ns_073.inc
===================================================================
RCS file: Zend/tests/ns_073.inc
diff -N Zend/tests/ns_073.inc
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_073.inc       12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,11 @@
+<?php
+namespace one;
+function hi()
+{
+echo "hi\n";
+}
+function parent()
+{
+echo "weird\n";
+}
+?>
\ No newline at end of file
Index: Zend/tests/ns_073.phpt
===================================================================
RCS file: Zend/tests/ns_073.phpt
diff -N Zend/tests/ns_073.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_073.phpt      12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,13 @@
+--TEST--
+073: use for namespace functions
+--FILE--
+<?php
+include __DIR__ . '/ns_073.inc';
+use function::one::hi, function::one::hi as there;
+hi();there();
+?>
+===DONE===
+--EXPECT--
+hi
+hi
+===DONE===
Index: Zend/tests/ns_074.phpt
===================================================================
RCS file: Zend/tests/ns_074.phpt
diff -N Zend/tests/ns_074.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_074.phpt      12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,11 @@
+--TEST--
+074: use static
+--FILE--
+<?php
+include __DIR__ . '/ns_073.inc';
+use function::one::parent;
+parent();
+?>
+===DONE===
+--EXPECTF--
+Fatal error: Cannot use one::parent as parent because 'parent' is a special 
class name in %sns_074.php on line %d
\ No newline at end of file
Index: Zend/tests/ns_075.phpt
===================================================================
RCS file: Zend/tests/ns_075.phpt
diff -N Zend/tests/ns_075.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_075.phpt      12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,11 @@
+--TEST--
+075: use for namespace functions
+--FILE--
+<?php
+use function::strlen as wow;
+echo wow("this is neat"),"\n";
+?>
+===DONE===
+--EXPECT--
+12
+===DONE===
Index: Zend/tests/ns_076.phpt
===================================================================
RCS file: Zend/tests/ns_076.phpt
diff -N Zend/tests/ns_076.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Zend/tests/ns_076.phpt      12 Sep 2008 01:57:04 -0000
@@ -0,0 +1,12 @@
+--TEST--
+076: use for namespace functions
+--FILE--
+<?php
+namespace one;
+use function::strlen as wow;
+echo wow("this is neat!"),"\n";
+?>
+===DONE===
+--EXPECT--
+13
+===DONE===

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to