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