Index: ext/reflection/php_reflection.c =================================================================== --- ext/reflection/php_reflection.c (revision 302209) +++ ext/reflection/php_reflection.c (working copy) @@ -676,8 +676,8 @@ if (arg_info->allow_null) { string_printf(str, "or NULL "); } - } else if (arg_info->array_type_hint) { - string_printf(str, "array "); + } else if (arg_info->type_hint) { + string_printf(str, "%s ", zend_get_type_by_const(arg_info->type_hint)); if (arg_info->allow_null) { string_printf(str, "or NULL "); } @@ -2282,10 +2282,105 @@ } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_BOOL(param->arg_info->array_type_hint); + RETVAL_BOOL(param->arg_info->type_hint == IS_ARRAY); } /* }}} */ +/* {{{ proto public bool ReflectionParameter::isInt() + Returns whether parameter MUST be a long */ +ZEND_METHOD(reflection_parameter, isInt) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_LONG); +} +/* }}} */ + +/* {{{ proto public bool ReflectionParameter::isDouble() + Returns whether parameter MUST be a double */ +ZEND_METHOD(reflection_parameter, isDouble) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_DOUBLE); +} +/* }}} */ + +/* {{{ proto public bool ReflectionParameter::isBool() + Returns whether parameter MUST be a boolean */ +ZEND_METHOD(reflection_parameter, isBool) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_BOOL); +} +/* }}} */ + +/* {{{ proto public bool ReflectionParameter::isObject() + Returns whether parameter MUST be a boolean */ +ZEND_METHOD(reflection_parameter, isObject) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_OBJECT); +} +/* }}} */ + +/* {{{ proto public bool ReflectionParameter::isString() + Returns whether parameter MUST be a string */ +ZEND_METHOD(reflection_parameter, isString) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_STRING); +} +/* }}} */ + +/* {{{ proto public bool ReflectionParameter::isResource() + Returns whether parameter MUST be a resource */ +ZEND_METHOD(reflection_parameter, isResource) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint == IS_RESOURCE); +} +/* }}} */ + +/* {{{ proto public string ReflectionParameter::getTypeHint() + Returns what type hint is defined for this parameter */ +ZEND_METHOD(reflection_parameter, getTypeHint) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + if (!param->arg_info->type_hint) { + RETURN_FALSE; + } + + RETURN_STRING(zend_get_type_by_const(param->arg_info->type_hint), 1); +} +/* }}} */ + /* {{{ proto public bool ReflectionParameter::allowsNull() Returns whether NULL is allowed as this parameters's value */ ZEND_METHOD(reflection_parameter, allowsNull) @@ -5009,7 +5104,7 @@ } /* }}} */ -/* {{{ proto public void ReflectionExtension::info() U +/* {{{ proto public void ReflectionExtension::info() Prints phpinfo block for the extension */ ZEND_METHOD(reflection_extension, info) { @@ -5356,6 +5451,13 @@ ZEND_ME(reflection_parameter, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getClass, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isArray, arginfo_reflection__void, 0) + ZEND_ME(reflection_parameter, isInt, NULL, 0) + ZEND_ME(reflection_parameter, isDouble, NULL, 0) + ZEND_ME(reflection_parameter, isBool, NULL, 0) + ZEND_ME(reflection_parameter, getTypeHint, NULL, 0) + ZEND_ME(reflection_parameter, isString, NULL, 0) + ZEND_ME(reflection_parameter, isObject, NULL, 0) + ZEND_ME(reflection_parameter, isResource, NULL, 0) ZEND_ME(reflection_parameter, allowsNull, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getPosition, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isOptional, arginfo_reflection__void, 0) Index: Zend/zend.h =================================================================== --- Zend/zend.h (revision 302209) +++ Zend/zend.h (working copy) @@ -518,6 +518,8 @@ #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 +/* used for type-hinting only */ +#define IS_CLASS 10 /* Ugly hack to support constants as static array indices */ #define IS_CONSTANT_TYPE_MASK 0x0f Index: Zend/zend_execute.c =================================================================== --- Zend/zend_execute.c (revision 302209) +++ Zend/zend_execute.c (working copy) @@ -504,13 +504,21 @@ need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC); } - } else if (cur_arg_info->array_type_hint) { + } else if (cur_arg_info->type_hint) { if (!arg) { - return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", "none", "" TSRMLS_CC); + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "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(zf, arg_num, cur_arg_info, "be an array", "", zend_zval_type_name(arg), "" TSRMLS_CC); + + /* existing type already matches the hint or forced type */ + if (Z_TYPE_P(arg) == cur_arg_info->type_hint) { + return 1; } + + /* NULL type given, check if parameter is optional */ + if (Z_TYPE_P(arg) == IS_NULL && cur_arg_info->allow_null) { + return 1; + } + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "", zend_zval_type_name(arg) TSRMLS_CC); } return 1; } Index: Zend/zend_language_scanner.l =================================================================== --- Zend/zend_language_scanner.l (revision 302209) +++ Zend/zend_language_scanner.l (working copy) @@ -1170,6 +1170,52 @@ return T_ARRAY; } +("bool"|"boolean"){TABS_AND_SPACES}[ \t&]+"$" { + if (yytext[4] == 'e') { + yyless((sizeof("boolean") - 1)); + } else { + yyless((sizeof("bool") - 1)); + } + return T_BOOL_HINT; +} + +("string"|"binary"){TABS_AND_SPACES}[ \t&]+"$" { + yyless(6); + return T_STRING_HINT; +} + +("int"|"integer"|"long"){TABS_AND_SPACES}[ \t&]+"$" { + if (yytext[3] == 'e') { + yyless((sizeof("integer") - 1)); + } else if (yytext[3] == 'g') { + yyless((sizeof("long") - 1)); + } else { + yyless(3); + } + return T_INT_HINT; +} + +("real"|"double"|"float"){TABS_AND_SPACES}[ \t&]+"$" { + if (yytext[4] == 'l') { + yyless((sizeof("double") - 1)); + } else if (yytext[4] == 't') { + yyless((sizeof("float") - 1)); + } else { + yyless(4); + } + return T_DOUBLE_HINT; +} + +"resource"{TABS_AND_SPACES}[ \t&]+"$" { + yyless((sizeof("resource") - 1)); + return T_RESOURCE_HINT; +} + +"object"{TABS_AND_SPACES}[ \t&]+"$" { + yyless((sizeof("object") - 1)); + return T_OBJECT_HINT; +} + "++" { return T_INC; } Index: Zend/zend_compile.c =================================================================== --- Zend/zend_compile.c (revision 302209) +++ Zend/zend_compile.c (working copy) @@ -1511,7 +1511,7 @@ cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1]; cur_arg_info->name = estrndup(varname->u.constant.value.str.val, varname->u.constant.value.str.len); cur_arg_info->name_len = varname->u.constant.value.str.len; - cur_arg_info->array_type_hint = 0; + cur_arg_info->type_hint = 0; cur_arg_info->allow_null = 1; cur_arg_info->pass_by_reference = pass_by_reference; cur_arg_info->class_name = NULL; @@ -1519,7 +1519,10 @@ if (class_type->op_type != IS_UNUSED) { cur_arg_info->allow_null = 0; - if (class_type->u.constant.type == IS_STRING) { + cur_arg_info->type_hint = class_type->u.constant.type; + + switch (class_type->u.constant.type) { + case IS_CLASS: 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); } @@ -1532,10 +1535,9 @@ zend_error(E_COMPILE_ERROR, "Default value for parameters with a class type hint can only be NULL"); } } - } else { - cur_arg_info->array_type_hint = 1; - cur_arg_info->class_name = NULL; - cur_arg_info->class_name_len = 0; + break; + + case 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; @@ -1543,6 +1545,26 @@ zend_error(E_COMPILE_ERROR, "Default value for parameters with array type hint can only be an array or NULL"); } } + break; + + /* scalar type hinting */ + case IS_BOOL: + case IS_STRING: + case IS_LONG: + case IS_DOUBLE: + case IS_RESOURCE: + case IS_OBJECT: + if (op == ZEND_RECV_INIT) { + if (Z_TYPE(initialization->u.constant) != class_type->u.constant.type && Z_TYPE(initialization->u.constant) != IS_NULL && (Z_TYPE(initialization->u.constant) & IS_CONSTANT_TYPE_MASK) != IS_CONSTANT) { + zend_error(E_COMPILE_ERROR, "Default value for parameters with %s type hint can only be %s or NULL", zend_get_type_by_const(class_type->u.constant.type), zend_get_type_by_const(class_type->u.constant.type)); + } else if (Z_TYPE(initialization->u.constant) == IS_NULL) { + cur_arg_info->allow_null = 1; + } + } + break; + + default: + zend_error(E_COMPILE_ERROR, "Unknown type hint"); } } opline->result.u.EA.type |= EXT_TYPE_UNUSED; @@ -2576,8 +2598,8 @@ return 0; } } - if (fe->common.arg_info[i].array_type_hint != proto->common.arg_info[i].array_type_hint) { - /* Only one has an array type hint and the other one doesn't */ + if (fe->common.arg_info[i].type_hint != proto->common.arg_info[i].type_hint) { + /* Incompatible type hint */ return 0; } if (fe->common.arg_info[i].pass_by_reference != proto->common.arg_info[i].pass_by_reference) { Index: Zend/zend_compile.h =================================================================== --- Zend/zend_compile.h (revision 302209) +++ Zend/zend_compile.h (working copy) @@ -175,7 +175,7 @@ zend_uint name_len; const char *class_name; zend_uint class_name_len; - zend_bool array_type_hint; + zend_uint type_hint; zend_bool allow_null; zend_bool pass_by_reference; zend_bool return_reference; Index: Zend/zend_API.c =================================================================== --- Zend/zend_API.c (revision 302209) +++ Zend/zend_API.c (working copy) @@ -206,6 +206,7 @@ case IS_STRING: return "string"; case IS_OBJECT: + case IS_CLASS: return "object"; case IS_RESOURCE: return "resource"; Index: Zend/zend_API.h =================================================================== --- Zend/zend_API.h (revision 302209) +++ Zend/zend_API.h (working copy) @@ -98,8 +98,9 @@ #define ZEND_ARG_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, 0, pass_by_ref, 0, 0 }, #define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, NULL, 0, 0, 0, pass_by_ref, 0, 0 }, -#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, 0, allow_null, pass_by_ref, 0, 0 }, -#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, 1, allow_null, pass_by_ref, 0, 0 }, +#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_CLASS, allow_null, pass_by_ref, 0, 0 }, +#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, allow_null, pass_by_ref, 0, 0 }, +#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, sizeof(#name)-1, NULL, 0, type_hint, allow_null, pass_by_ref, 0, 0 }, #define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ static const zend_arg_info name[] = { \ { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, Index: Zend/zend_language_parser.y =================================================================== --- Zend/zend_language_parser.y (revision 302209) +++ Zend/zend_language_parser.y (working copy) @@ -128,6 +128,12 @@ %token T_DOUBLE_ARROW %token T_LIST %token T_ARRAY +%token T_BOOL_HINT +%token T_STRING_HINT +%token T_INT_HINT +%token T_DOUBLE_HINT +%token T_RESOURCE_HINT +%token T_OBJECT_HINT %token T_CLASS_C %token T_METHOD_C %token T_FUNC_C @@ -463,8 +469,14 @@ optional_class_type: /* empty */ { $$.op_type = IS_UNUSED; } - | fully_qualified_class_name { $$ = $1; } - | T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_NULL;} + | T_ARRAY { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; } + | T_BOOL_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_BOOL; } + | T_STRING_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_STRING; } + | T_INT_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_LONG; } + | T_DOUBLE_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_DOUBLE; } + | T_RESOURCE_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_RESOURCE; } + | T_OBJECT_HINT { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_OBJECT; } + | fully_qualified_class_name { $$ = $1; $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_CLASS; } ;