Common subdirectories: ../php-5.2.5/Zend/RFCs and Zend/RFCs Common subdirectories: ../php-5.2.5/Zend/tests and Zend/tests diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_API.c Zend/zend_API.c --- ../php-5.2.5/Zend/zend_API.c 2007-08-31 08:36:13.000000000 -0400 +++ Zend/zend_API.c 2008-04-24 17:33:49.000000000 -0400 @@ -1663,6 +1663,16 @@ internal_function->function_name = ptr->fname; internal_function->scope = scope; internal_function->prototype = NULL; + + // Return value type hinting + + internal_function->return_type_hint = 0; + internal_function->return_scalar_type_hint = 0; + internal_function->return_num_type_hint = 0; + internal_function->return_array_type_hint = 0; + internal_function->return_class_type_hint_name = NULL; + internal_function->return_class_type_hint_len = 0; + if (ptr->arg_info) { internal_function->arg_info = ptr->arg_info+1; internal_function->num_args = ptr->num_args; diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_API.h Zend/zend_API.h --- ../php-5.2.5/Zend/zend_API.h 2007-04-16 04:09:54.000000000 -0400 +++ Zend/zend_API.h 2008-04-24 17:48:54.000000000 -0400 @@ -69,7 +69,7 @@ #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_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ zend_arg_info name[] = { \ - { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, + { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args, 0, 0, 0 }, #define ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference) \ ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, ZEND_RETURN_VALUE, -1) #define ZEND_END_ARG_INFO() }; diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_compile.c Zend/zend_compile.c --- ../php-5.2.5/Zend/zend_compile.c 2007-09-20 10:11:31.000000000 -0400 +++ Zend/zend_compile.c 2008-04-25 10:56:51.000000000 -0400 @@ -1060,7 +1060,7 @@ } -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode, znode *return_type_hint TSRMLS_DC) { zend_op_array op_array; char *name = function_name->u.constant.value.str.val; @@ -1069,7 +1069,7 @@ zend_uint fn_flags; char *lcname; zend_bool orig_interactive; - + if (is_method) { if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { if ((Z_LVAL(fn_flags_znode->u.constant) & ~(ZEND_ACC_STATIC|ZEND_ACC_PUBLIC))) { @@ -1103,6 +1103,43 @@ op_array.line_start = zend_get_compiled_lineno(TSRMLS_C); + // Apply return value type hinting + + op_array.return_scalar_type_hint = 0; + op_array.return_num_type_hint = 0; + op_array.return_type_hint = 0; + op_array.return_array_type_hint = 0; + op_array.return_class_type_hint_name = NULL; + op_array.return_class_type_hint_len = 0; + + if (return_type_hint->op_type != IS_UNUSED) { + if (return_type_hint->u.constant.type == IS_STRING) { + if (strcmp("scalar", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_scalar_type_hint = 1; + } else if (strcmp("num", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_num_type_hint = 1; + } else if (strcmp("int", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_LONG; + } else if (strcmp("float", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_DOUBLE; + } else if (strcmp("string", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_STRING; + } else if (strcmp("bool", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_BOOL; + } else if (strcmp("object", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_OBJECT; + } else if (strcmp("resource", return_type_hint->u.constant.value.str.val) == 0) { + op_array.return_type_hint = IS_RESOURCE; + } else { + op_array.return_class_type_hint_name = return_type_hint->u.constant.value.str.val; + op_array.return_class_type_hint_len = return_type_hint->u.constant.value.str.len; + } + } + else { + op_array.return_array_type_hint = 1; + } + } + if (is_method) { char *short_class_name = CG(active_class_entry)->name; int short_class_name_length = CG(active_class_entry)->name_length; @@ -1292,37 +1329,45 @@ 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->allow_null = 1; cur_arg_info->pass_by_reference = pass_by_reference; - + cur_arg_info->array_type_hint = 0; + cur_arg_info->type_hint = 0; + cur_arg_info->scalar_type_hint = 0; + cur_arg_info->num_type_hint = 0; + cur_arg_info->class_name = NULL; + cur_arg_info->class_name_len = 0; if (class_type->op_type != IS_UNUSED) { cur_arg_info->allow_null = 0; + 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; + } + } if (class_type->u.constant.type == IS_STRING) { - 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 (strcmp("scalar", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->scalar_type_hint = 1; + } else if (strcmp("num", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->num_type_hint = 1; + } else if (strcmp("int", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_LONG; + } else if (strcmp("float", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_DOUBLE; + } else if (strcmp("string", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_STRING; + } else if (strcmp("bool", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_BOOL; + } else if (strcmp("object", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_OBJECT; + } else if (strcmp("resource", class_type->u.constant.value.str.val) == 0) { + cur_arg_info->type_hint = IS_RESOURCE; + } else { + 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; } } else { cur_arg_info->array_type_hint = 1; - cur_arg_info->class_name = NULL; - cur_arg_info->class_name_len = 0; - 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->class_name = NULL; - cur_arg_info->class_name_len = 0; } opline->result.u.EA.type |= EXT_TYPE_UNUSED; } @@ -1984,6 +2029,27 @@ return 0; } + // Check return value type hints + + if (ZEND_LOG_XOR(fe->common.return_class_type_hint_name, proto->common.return_class_type_hint_name)) { + return 0; + } + if (fe->common.return_class_type_hint_name && strcmp(fe->common.return_class_type_hint_name, proto->common.return_class_type_hint_name) != 0) { + return 0; + } + if (fe->common.return_scalar_type_hint != proto->common.return_scalar_type_hint) { + return 0; + } + if (fe->common.return_num_type_hint != proto->common.return_num_type_hint) { + return 0; + } + if (fe->common.return_type_hint != proto->common.return_type_hint) { + return 0; + } + if (fe->common.return_array_type_hint != proto->common.return_array_type_hint) { + return 0; + } + for (i=0; i < proto->common.num_args; i++) { if (ZEND_LOG_XOR(fe->common.arg_info[i].class_name, proto->common.arg_info[i].class_name)) { /* Only one has a type hint and the other one doesn't */ @@ -1993,6 +2059,15 @@ && strcmp(fe->common.arg_info[i].class_name, proto->common.arg_info[i].class_name)!=0) { return 0; } + if (fe->common.arg_info[i].scalar_type_hint != proto->common.arg_info[i].scalar_type_hint) { + return 0; + } + if (fe->common.arg_info[i].num_type_hint != proto->common.arg_info[i].num_type_hint) { + return 0; + } + if (fe->common.arg_info[i].type_hint != proto->common.arg_info[i].type_hint) { + 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 */ return 0; Only in Zend: zend_compile.c.orig diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_compile.h Zend/zend_compile.h --- ../php-5.2.5/Zend/zend_compile.h 2007-05-18 09:12:04.000000000 -0400 +++ Zend/zend_compile.h 2008-04-25 08:11:46.000000000 -0400 @@ -167,6 +167,9 @@ zend_bool pass_by_reference; zend_bool return_reference; int required_num_args; + zend_bool scalar_type_hint; + zend_bool num_type_hint; + zend_uint type_hint; } zend_arg_info; typedef struct _zend_compiled_variable { @@ -187,6 +190,16 @@ zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; + + // Return value type hinting + + zend_uint return_type_hint; + zend_uint return_scalar_type_hint; + zend_uint return_num_type_hint; + zend_uint return_array_type_hint; + char *return_class_type_hint_name; + zend_uint return_class_type_hint_len; + /* END of common elements */ zend_uint *refcount; @@ -240,6 +253,16 @@ zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; + + // Return value type hinting + + zend_uint return_type_hint; + zend_uint return_scalar_type_hint; + zend_uint return_num_type_hint; + zend_uint return_array_type_hint; + char *return_class_type_hint_name; + zend_uint return_class_type_hint_len; + /* END of common elements */ void (*handler)(INTERNAL_FUNCTION_PARAMETERS); @@ -262,6 +285,16 @@ zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; + + // Return value type hinting + + zend_uint return_type_hint; + zend_uint return_scalar_type_hint; + zend_uint return_num_type_hint; + zend_uint return_array_type_hint; + char *return_class_type_hint_name; + zend_uint return_class_type_hint_len; + } common; zend_op_array op_array; @@ -396,7 +429,7 @@ void zend_do_add_variable(znode *result, znode *op1, znode *op2 TSRMLS_DC); int zend_do_verify_access_types(znode *current_access_type, znode *new_modifier); -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC); +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode, znode *return_type_hint TSRMLS_DC); void zend_do_end_function_declaration(znode *function_token TSRMLS_DC); void zend_do_receive_arg(zend_uchar op, znode *var, znode *offset, znode *initialization, znode *class_type, znode *varname, zend_bool pass_by_reference TSRMLS_DC); int zend_do_begin_function_call(znode *function_name TSRMLS_DC); Only in Zend: zend_config.h diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_constants.c Zend/zend_constants.c --- ../php-5.2.5/Zend/zend_constants.c 2007-07-27 12:29:11.000000000 -0400 +++ Zend/zend_constants.c 2008-04-24 17:48:54.000000000 -0400 @@ -135,6 +135,12 @@ c.value.type = IS_NULL; zend_register_constant(&c TSRMLS_CC); + c.name = zend_strndup(ZEND_STRL("PATCH_SCALAR_TYPE_HINTING")); + c.name_len = sizeof("PATCH_SCALAR_TYPE_HINTING"); + c.value.value.lval = 1; + c.value.type = IS_BOOL; + zend_register_constant(&c TSRMLS_CC); + c.flags = CONST_PERSISTENT; c.name = zend_strndup(ZEND_STRL("ZEND_THREAD_SAFE")); Only in Zend: zend_constants.c.orig diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_execute.c Zend/zend_execute.c --- ../php-5.2.5/Zend/zend_execute.c 2007-07-19 11:29:30.000000000 -0400 +++ Zend/zend_execute.c 2008-04-24 18:25:24.000000000 -0400 @@ -480,6 +480,29 @@ return 0; } +static inline int zend_verify_arg_default_error(zend_function *zf, zend_uint arg_num, zend_arg_info *cur_arg_info, char *need_msg, char *need_kind, char *given_msg, char *given_kind TSRMLS_DC) +{ + zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; + char *fname = zf->common.function_name; + char *fsep; + char *fclass; + + if (zf->common.scope) { + fsep = "::"; + fclass = zf->common.scope->name; + } else { + fsep = ""; + fclass = ""; + } + + if (ptr && ptr->op_array) { + zend_error(E_RECOVERABLE_ERROR, "Default for argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno); + } else { + zend_error(E_RECOVERABLE_ERROR, "Default for argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + } + return 0; +} + static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg TSRMLS_DC) { zend_arg_info *cur_arg_info; @@ -509,6 +532,48 @@ need_msg = zend_verify_arg_class_kind(cur_arg_info, &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->scalar_type_hint) { + if (!arg) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of a scalar type", "", "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { + if (Z_TYPE_P(arg) == IS_OBJECT) { + if (!Z_OBJCE_P(arg)->__tostring) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of a scalar type or an object with a __toString method", "", "object without a __toString method", "" TSRMLS_CC); + } + } + else if (Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE && Z_TYPE_P(arg) != IS_BOOL && Z_TYPE_P(arg) != IS_STRING) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of a scalar type or an object with a __toString method", "", zend_zval_type_name(arg), "" TSRMLS_CC); + } + } + } else if (cur_arg_info->num_type_hint) { + if (!arg) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be a number", "", "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be a number", "", zend_zval_type_name(arg), "" TSRMLS_CC); + } + } else if (cur_arg_info->type_hint) { + if (!arg) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { + if (cur_arg_info->type_hint == IS_STRING) { + if (Z_TYPE_P(arg) != IS_STRING) { + if (Z_TYPE_P(arg) == IS_OBJECT) { + if (!Z_OBJCE_P(arg)->__tostring) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be a string or an object with a __toString method", "", "object without a __toString method", "" TSRMLS_CC); + } + } + else { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be a string or an object with a __toString method", "", zend_zval_type_name(arg), "" TSRMLS_CC); + } + } + } + else if (Z_TYPE_P(arg) != cur_arg_info->type_hint) { + return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be of type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "" TSRMLS_CC); + } + } } else if (cur_arg_info->array_type_hint) { if (!arg) { return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", "none", "" TSRMLS_CC); @@ -521,6 +586,113 @@ } +static inline char * zend_verify_return_class_kind(zend_function *zf, char **class_name, zend_class_entry **pce TSRMLS_DC) +{ + *pce = zend_fetch_class(zf->common.return_class_type_hint_name, zf->common.return_class_type_hint_len, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC); + + *class_name = (*pce) ? (*pce)->name: zf->common.return_class_type_hint_name; + if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) { + return "implement interface "; + } else { + return "be an instance of "; + } +} + + +static inline int zend_verify_return_error(zend_function *zf, char *need_msg, char *need_kind, char *given_msg, char *given_kind TSRMLS_DC) +{ + zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; + char *fname = zf->common.function_name; + char *fsep; + char *fclass; + + if (zf->common.scope) { + fsep = "::"; + fclass = zf->common.scope->name; + } else { + fsep = ""; + fclass = ""; + } + + if (ptr && ptr->op_array) { + zend_error(E_RECOVERABLE_ERROR, "Value returned from function %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno); + } else { + zend_error(E_RECOVERABLE_ERROR, "Value returned from function %s%s%s() must %s%s, %s%s given", fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + } + return 0; +} + + +static inline int zend_verify_return_type(zend_function *zf, zval *return_value TSRMLS_DC) +{ + char *need_msg; + zend_class_entry *ce; + + if (zf->common.return_class_type_hint_name) { + char *class_name; + if (!return_value) { + need_msg = zend_verify_return_class_kind(zf, &class_name, &ce TSRMLS_CC); + return zend_verify_return_error(zf, need_msg, class_name, "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(return_value) == IS_OBJECT) { + need_msg = zend_verify_return_class_kind(zf, &class_name, &ce TSRMLS_CC); + if (!ce || !instanceof_function(Z_OBJCE_P(return_value), ce TSRMLS_CC)) { + return zend_verify_return_error(zf, need_msg, class_name, "instance of ", Z_OBJCE_P(return_value)->name TSRMLS_CC); + } + } else { + need_msg = zend_verify_return_class_kind(zf, &class_name, &ce TSRMLS_CC); + return zend_verify_return_error(zf, need_msg, class_name, zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } else if (zf->common.return_scalar_type_hint) { + if (!return_value) { + return zend_verify_return_error(zf, "be of a scalar type", "", "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(return_value) == IS_OBJECT) { + if (!Z_OBJCE_P(return_value)->__tostring) { + return zend_verify_return_error(zf, "be of a scalar type or an object with a __toString method", "", "object without a __toString method", "" TSRMLS_CC); + } + } + else if (Z_TYPE_P(return_value) != IS_LONG && Z_TYPE_P(return_value) != IS_DOUBLE && Z_TYPE_P(return_value) != IS_BOOL && Z_TYPE_P(return_value) != IS_STRING) { + return zend_verify_return_error(zf, "be of a scalar type or an object with a __toString method", "", zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } else if (zf->common.return_num_type_hint) { + if (!return_value) { + return zend_verify_return_error(zf, "be a number", "", "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(return_value) != IS_LONG && Z_TYPE_P(return_value) != IS_DOUBLE) { + return zend_verify_return_error(zf, "be a number", "", zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } else if (zf->common.return_type_hint) { + if (!return_value) { + return zend_verify_return_error(zf, "be of type ", zend_get_type_by_const(zf->common.return_type_hint), "none", "" TSRMLS_CC); + } + if (zf->common.return_type_hint == IS_STRING) { + if (Z_TYPE_P(return_value) != IS_STRING) { + if (Z_TYPE_P(return_value) == IS_OBJECT) { + if (!Z_OBJCE_P(return_value)->__tostring) { + return zend_verify_return_error(zf, "be a string or an object with a __toString method", "", "object without a __toString method", "" TSRMLS_CC); + } + } + else { + return zend_verify_return_error(zf, "be a string or an object with a __toString method", "", zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } + } + else if (Z_TYPE_P(return_value) != zf->common.return_type_hint) { + return zend_verify_return_error(zf, "be of type ", zend_get_type_by_const(zf->common.return_type_hint), zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } else if (zf->common.return_array_type_hint) { + if (!return_value) { + return zend_verify_return_error(zf, "be an array", "", "none", "" TSRMLS_CC); + } + if (Z_TYPE_P(return_value) != IS_ARRAY) { + return zend_verify_return_error(zf, "be an array", "", zend_zval_type_name(return_value), "" TSRMLS_CC); + } + } + return 1; +} + + static inline void zend_assign_to_object(znode *result, zval **object_ptr, znode *op2, znode *value_op, temp_variable *Ts, int opcode TSRMLS_DC) { zval *object; diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_language_parser.y Zend/zend_language_parser.y --- ../php-5.2.5/Zend/zend_language_parser.y 2008-04-24 13:59:37.000000000 -0400 +++ Zend/zend_language_parser.y 2008-04-25 08:11:16.000000000 -0400 @@ -108,6 +108,7 @@ %token T_FUNCTION %token T_CONST %token T_RETURN +%token T_RETURNS %token T_TRY %token T_CATCH %token T_THROW @@ -278,8 +279,14 @@ ; +optional_function_return_type: + /* empty */ { $$.op_type = IS_UNUSED; } + | T_RETURNS T_STRING { $$ = $2; } + | T_RETURNS T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_NULL;} +; + unticked_function_declaration_statement: - T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type, NULL TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } + T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING optional_function_return_type { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type, NULL, &$5 TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; unticked_class_declaration_statement: @@ -486,8 +493,8 @@ class_statement: variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_declaration ';' | class_constant_declaration ';' - | method_modifiers T_FUNCTION { $2.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $4.op_type, &$1 TSRMLS_CC); } '(' - parameter_list ')' method_body { zend_do_abstract_method(&$5, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } + | method_modifiers T_FUNCTION { $2.u.opline_num = CG(zend_lineno); } is_reference T_STRING optional_function_return_type { zend_do_begin_function_declaration(&$2, &$5, 1, $4.op_type, &$1, &$6 TSRMLS_CC); } '(' + parameter_list ')' method_body { zend_do_abstract_method(&$5, &$1, &$11 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_language_scanner.l Zend/zend_language_scanner.l --- ../php-5.2.5/Zend/zend_language_scanner.l 2007-09-09 12:33:34.000000000 -0400 +++ Zend/zend_language_scanner.l 2008-04-24 16:36:04.000000000 -0400 @@ -989,6 +989,10 @@ return T_RETURN; } +"returns" { + return T_RETURNS; +} + "try" { return T_TRY; } diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_opcode.c Zend/zend_opcode.c --- ../php-5.2.5/Zend/zend_opcode.c 2007-04-16 04:09:55.000000000 -0400 +++ Zend/zend_opcode.c 2008-04-24 17:35:59.000000000 -0400 @@ -101,6 +101,15 @@ op_array->fn_flags = CG(interactive)?ZEND_ACC_INTERACTIVE:0; + // Return value type hinting + + op_array->return_type_hint = 0; + op_array->return_scalar_type_hint = 0; + op_array->return_num_type_hint = 0; + op_array->return_array_type_hint = 0; + op_array->return_class_type_hint_name = NULL; + op_array->return_class_type_hint_len = 0; + zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array TSRMLS_CC); } diff -u --exclude-from=patch.ignore ../php-5.2.5/Zend/zend_vm_def.h Zend/zend_vm_def.h --- ../php-5.2.5/Zend/zend_vm_def.h 2007-10-04 19:23:41.000000000 -0400 +++ Zend/zend_vm_def.h 2008-04-24 18:22:52.000000000 -0400 @@ -2092,8 +2092,9 @@ (*retval_ptr_ptr)->refcount++; (*EG(return_value_ptr_ptr)) = (*retval_ptr_ptr); + } else { -ZEND_VM_C_LABEL(return_by_value): + ZEND_VM_C_LABEL(return_by_value): retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -2135,6 +2136,10 @@ INIT_PZVAL_COPY(ret, retval_ptr); *EG(return_value_ptr_ptr) = ret; } + + // Check return value against type hint + zend_verify_return_type(EX(function_state).function, (EG(return_value_ptr_ptr) ? *EG(return_value_ptr_ptr) : retval_ptr) TSRMLS_CC); + } FREE_OP1_IF_VAR(); ZEND_VM_RETURN_FROM_EXECUTE_LOOP();