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