Hi

As quickly mentioned in the '$arr = array('Hello', 'world'); $arr();'
thread[1], we are hitting the need for a callable typehint.


See attached patch+phpt; Any objections to include it in 5.4?

-Hannes

[1] http://php.markmail.org/message/gdas65h3im52sleg
Index: Zend/zend.h
===================================================================
--- Zend/zend.h (revision 311867)
+++ Zend/zend.h (working copy)
@@ -573,6 +573,7 @@
 #define IS_RESOURCE    7
 #define IS_CONSTANT    8
 #define IS_CONSTANT_ARRAY      9
+#define IS_CALLABLE    10
 
 /* Ugly hack to support constants as static array indices */
 #define IS_CONSTANT_TYPE_MASK          0x00f
Index: Zend/zend_execute.c
===================================================================
--- Zend/zend_execute.c (revision 311867)
+++ Zend/zend_execute.c (working copy)
@@ -626,13 +626,24 @@
                        need_msg = zend_verify_arg_class_kind(cur_arg_info, 
fetch_type, &class_name, &ce TSRMLS_CC);
                        return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, 
arg_num, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC);
                }
-       } else if (cur_arg_info->type_hint && cur_arg_info->type_hint == 
IS_ARRAY) {
-               if (!arg) {
-                       return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, 
arg_num, "be of the type array", "", "none", "" TSRMLS_CC);
-               }
+       } else if (cur_arg_info->type_hint) {
+               switch(cur_arg_info->type_hint) {
+                       case IS_ARRAY:
+                       if (!arg) {
+                               return 
zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", 
"", "none", "" TSRMLS_CC);
+                       }
 
-               if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || 
!cur_arg_info->allow_null)) {
-                       return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, 
arg_num, "be of the type array", "", zend_zval_type_name(arg), "" TSRMLS_CC);
+                       if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != 
IS_NULL || !cur_arg_info->allow_null)) {
+                               return 
zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", 
"", zend_zval_type_name(arg), "" TSRMLS_CC);
+                       }
+                       break;
+                       case IS_CALLABLE:
+                       if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, 
NULL) && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) {
+                               return 
zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", 
zend_zval_type_name(arg), "" TSRMLS_CC);
+                       }
+                       break;
+                       default:
+                       zend_error(E_ERROR, "Unknown typehint");
                }
        }
        return 1;
Index: Zend/zend_language_scanner.l
===================================================================
--- Zend/zend_language_scanner.l        (revision 311867)
+++ Zend/zend_language_scanner.l        (working copy)
@@ -1310,6 +1310,10 @@
        return T_ARRAY;
 }
 
+<ST_IN_SCRIPTING>"callable" {
+ return T_CALLABLE;
+}
+
 <ST_IN_SCRIPTING>"++" {
        return T_INC;
 }
Index: Zend/zend_compile.c
===================================================================
--- Zend/zend_compile.c (revision 311867)
+++ Zend/zend_compile.c (working copy)
@@ -1849,28 +1849,40 @@
                cur_arg_info->allow_null = 0;
 
                if (class_type->u.constant.type != IS_NULL) {
-                       cur_arg_info->type_hint = IS_OBJECT;
-                       if (ZEND_FETCH_CLASS_DEFAULT == 
zend_get_class_fetch_type(Z_STRVAL(class_type->u.constant), 
Z_STRLEN(class_type->u.constant))) {
-                               zend_resolve_class_name(class_type, 
&opline->extended_value, 1 TSRMLS_CC);
-                       }
-                       class_type->u.constant.value.str.val = 
zend_new_interned_string(class_type->u.constant.value.str.val, 
class_type->u.constant.value.str.len + 1, 1 TSRMLS_CC);
-                       cur_arg_info->class_name = 
class_type->u.constant.value.str.val;
-                       cur_arg_info->class_name_len = 
class_type->u.constant.value.str.len;
-                       if (op == ZEND_RECV_INIT) {
-                               if (Z_TYPE(initialization->u.constant) == 
IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && 
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
-                                       cur_arg_info->allow_null = 1;
-                               } else {
-                                       zend_error(E_COMPILE_ERROR, "Default 
value for parameters with a class type hint can only be NULL");
+                       if (class_type->u.constant.type == IS_ARRAY) {
+                               cur_arg_info->type_hint = IS_ARRAY;
+                               if (op == ZEND_RECV_INIT) {
+                                       if (Z_TYPE(initialization->u.constant) 
== IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && 
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
+                                               cur_arg_info->allow_null = 1;
+                                       } else if 
(Z_TYPE(initialization->u.constant) != IS_ARRAY && 
Z_TYPE(initialization->u.constant) != IS_CONSTANT_ARRAY) {
+                                               zend_error(E_COMPILE_ERROR, 
"Default value for parameters with array type hint can only be an array or 
NULL");
+                                       }
                                }
-                       }
-               } else {
-                       cur_arg_info->type_hint = IS_ARRAY;
-                       if (op == ZEND_RECV_INIT) {
-                               if (Z_TYPE(initialization->u.constant) == 
IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && 
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
-                                       cur_arg_info->allow_null = 1;
-                               } else if (Z_TYPE(initialization->u.constant) 
!= IS_ARRAY && Z_TYPE(initialization->u.constant) != IS_CONSTANT_ARRAY) {
-                                       zend_error(E_COMPILE_ERROR, "Default 
value for parameters with array type hint can only be an array or NULL");
+                       } else if (class_type->u.constant.type == IS_CALLABLE) {
+                char *callable_name;
+                               cur_arg_info->type_hint = IS_CALLABLE;
+                               if (op == ZEND_RECV_INIT) {
+                                       if (Z_TYPE(initialization->u.constant) 
== IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && 
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
+                                               cur_arg_info->allow_null = 1;
+                                       } else {
+                                               zend_error(E_COMPILE_ERROR, 
"Default value for parameters with callable type hint can only be NULL");
+                                       }
                                }
+                       } else {
+                               cur_arg_info->type_hint = IS_OBJECT;
+                               if (ZEND_FETCH_CLASS_DEFAULT == 
zend_get_class_fetch_type(Z_STRVAL(class_type->u.constant), 
Z_STRLEN(class_type->u.constant))) {
+                                       zend_resolve_class_name(class_type, 
&opline->extended_value, 1 TSRMLS_CC);
+                               }
+                               class_type->u.constant.value.str.val = 
zend_new_interned_string(class_type->u.constant.value.str.val, 
class_type->u.constant.value.str.len + 1, 1 TSRMLS_CC);
+                               cur_arg_info->class_name = 
class_type->u.constant.value.str.val;
+                               cur_arg_info->class_name_len = 
class_type->u.constant.value.str.len;
+                               if (op == ZEND_RECV_INIT) {
+                                       if (Z_TYPE(initialization->u.constant) 
== IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && 
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
+                                               cur_arg_info->allow_null = 1;
+                                       } else {
+                                               zend_error(E_COMPILE_ERROR, 
"Default value for parameters with a class type hint can only be NULL");
+                                       }
+                               }
                        }
                }
        }
Index: Zend/zend_language_parser.y
===================================================================
--- Zend/zend_language_parser.y (revision 311867)
+++ Zend/zend_language_parser.y (working copy)
@@ -130,6 +130,7 @@
 %token T_DOUBLE_ARROW
 %token T_LIST
 %token T_ARRAY
+%token T_CALLABLE
 %token T_CLASS_C
 %token T_METHOD_C
 %token T_FUNC_C
@@ -466,7 +467,8 @@
 
 optional_class_type:
                /* empty */                                     { $$.op_type = 
IS_UNUSED; }
-       |       T_ARRAY                                         { $$.op_type = 
IS_CONST; Z_TYPE($$.u.constant)=IS_NULL; }
+       |       T_ARRAY                                         { $$.op_type = 
IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; }
+       |       T_CALLABLE                              { $$.op_type = 
IS_CONST; Z_TYPE($$.u.constant)=IS_CALLABLE; }
        |       fully_qualified_class_name                      { $$ = $1; }
 ;
 
Index: Zend/tests/callable_type_hint_001.phpt
===================================================================
--- Zend/tests/callable_type_hint_001.phpt      (revision 0)
+++ Zend/tests/callable_type_hint_001.phpt      (revision 0)
@@ -0,0 +1,40 @@
+--TEST--
+callable type hint#001
+--FILE--
+<?php
+
+class bar {
+    function baz() {}
+    static function foo() {}
+}
+function foo(callable $bar) {
+    var_dump($bar);
+}
+$closure = function () {};
+
+foo("strpos");
+foo("foo");
+foo(array("bar", "baz"));
+foo(array("bar", "foo"));
+foo($closure);
+--EXPECTF--
+string(6) "strpos"
+string(3) "foo"
+
+Strict Standards: Non-static method bar::baz() should not be called statically 
in %scallable_type_hint_001.php on line %d
+array(2) {
+  [0]=>
+  string(3) "bar"
+  [1]=>
+  string(3) "baz"
+}
+array(2) {
+  [0]=>
+  string(3) "bar"
+  [1]=>
+  string(3) "foo"
+}
+object(Closure)#%d (0) {
+}
+
+
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to