Attached is my return type hinting patch. It allows type hinting for
parameters and return values. Int, float, bool, string, num, scalar,
resource, object, array, and class names are supported for both
parameters and return values. Objects with __toString methods are
allowed to pass for scalar and string type hints.

function myTypedFunction returns int(string $stuff, bool, $things, num
$amount) {
        return 'This will cause an error.'
}

I want to move the returns clause after the parameter list, but I
couldn't get it to work in the parser:

function x($param) returns y {
        return new y
}

Parameters and return values are strict. Int means int (to the type),
etc. For type hinted parameters, null is allowed only if it is the
default value.

Implementations of abstract functions must be compatible both in their
parameter and return types. Mismatched types will cause an error.

Note: The parameter type hinting is complete. The return type hinting
works, but there is a small memory leak which I do not know how to fix.
Besides that it's running perfectly, it's very minor obstacle, I'm just
not familiar enough with C to fix this right now.

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;
 }
 
+<ST_IN_SCRIPTING>"returns" {
+	return T_RETURNS;
+}
+
 <ST_IN_SCRIPTING>"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();

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to