Skip to content

Instantly share code, notes, and snippets.

@ircmaxell
Created March 3, 2012 02:54
Show Gist options
  • Save ircmaxell/1963999 to your computer and use it in GitHub Desktop.
Save ircmaxell/1963999 to your computer and use it in GitHub Desktop.
Scalar type hint/casting patch 2
Index: Zend/zend.h
===================================================================
--- Zend/zend.h (revision 323850)
+++ Zend/zend.h (working copy)
@@ -575,6 +575,15 @@
#define IS_CONSTANT_ARRAY 9
#define IS_CALLABLE 10
+/* Data type casts, hacked to be different from data type constants */
+#define IS_NULL_CAST 0x010
+#define IS_LONG_CAST 0x011
+#define IS_DOUBLE_CAST 0x012
+#define IS_BOOL_CAST 0x013
+#define IS_ARRAY_CAST 0x014
+#define IS_OBJECT_CAST 0x015
+#define IS_STRING_CAST 0x016
+
/* Ugly hack to support constants as static array indices */
#define IS_CONSTANT_TYPE_MASK 0x00f
#define IS_CONSTANT_UNQUALIFIED 0x010
Index: Zend/zend_execute.c
===================================================================
--- Zend/zend_execute.c (revision 323850)
+++ Zend/zend_execute.c (working copy)
@@ -597,12 +597,29 @@
return 0;
}
-static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC)
+/* We need an inline function here to wrap the convert_to_string MACRO to be usable in place of all the other convert_to_* functions */
+static inline void zend_convert_to_string_cast(zval *op) {
+ convert_to_string(op);
+}
+
+static inline int zend_verify_arg_type_cast(zend_function *zf, zend_uint arg_num, zend_arg_info *cur_arg_info, zval **arg, int type, char *msg, void (*convert_func)(zval *)) {
+ if (!*arg) {
+ return zend_verify_arg_error(E_WARNING, zf, arg_num, msg, "", "none", "" TSRMLS_CC);
+ } else if (type != Z_TYPE_PP(arg) && !(Z_TYPE_PP(arg) == IS_NULL && cur_arg_info->allow_null)) {
+ if (Z_ISREF_PP(arg)) {
+ return zend_verify_arg_error(E_CORE_ERROR, zf, arg_num, "cannot cast referenced parameter", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ SEPARATE_ZVAL(arg);
+ (*convert_func)(*arg);
+ }
+ return SUCCESS;
+}
+
+static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval **arg, ulong fetch_type TSRMLS_DC)
{
zend_arg_info *cur_arg_info;
char *need_msg;
zend_class_entry *ce;
-
if (!zf->common.arg_info
|| arg_num>zf->common.num_args) {
return 1;
@@ -613,40 +630,50 @@
if (cur_arg_info->class_name) {
const char *class_name;
- if (!arg) {
+ if (!*arg) {
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, "none", "" TSRMLS_CC);
}
- if (Z_TYPE_P(arg) == IS_OBJECT) {
+ if (Z_TYPE_PP(arg) == IS_OBJECT) {
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
- if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {
- return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);
+ if (!ce || !instanceof_function(Z_OBJCE_PP(arg), ce TSRMLS_CC)) {
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_PP(arg)->name TSRMLS_CC);
}
- } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
+ } else if (Z_TYPE_PP(arg) != IS_NULL || !cur_arg_info->allow_null) {
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);
+ 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) {
switch(cur_arg_info->type_hint) {
case IS_ARRAY:
- if (!arg) {
+ 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_PP(arg) != IS_ARRAY && (Z_TYPE_PP(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 (!arg) {
+ if (!*arg) {
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", "none", "" TSRMLS_CC);
}
- if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL TSRMLS_CC) && (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);
+ if (!zend_is_callable(*arg, IS_CALLABLE_CHECK_SILENT, NULL TSRMLS_CC) && (Z_TYPE_PP(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;
-
+ case IS_DOUBLE_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_DOUBLE, "be of the type float", &convert_to_double);
+ case IS_BOOL_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_BOOL, "be of the type boolean", &convert_to_boolean);
+ case IS_STRING_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_STRING, "be of the type string", &zend_convert_to_string_cast);
+ case IS_LONG_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_LONG, "be of the type integer", &convert_to_long);
+ case IS_ARRAY_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_ARRAY, "be of the type array", &convert_to_array);
+ case IS_OBJECT_CAST:
+ return zend_verify_arg_type_cast(zf, arg_num, cur_arg_info, arg, IS_OBJECT, "be of the type object", &convert_to_object);
default:
zend_error(E_ERROR, "Unknown typehint");
}
Index: Zend/zend_execute.h
===================================================================
--- Zend/zend_execute.h (revision 323850)
+++ Zend/zend_execute.h (working copy)
@@ -180,6 +180,7 @@
} \
} while (0)
+
static zend_always_inline zend_vm_stack zend_vm_stack_new_page(int count) {
zend_vm_stack page = (zend_vm_stack)emalloc(ZEND_MM_ALIGNED_SIZE(sizeof(*page)) + sizeof(void*) * count);
Index: Zend/zend_compile.c
===================================================================
--- Zend/zend_compile.c (revision 323850)
+++ Zend/zend_compile.c (working copy)
@@ -1822,6 +1822,20 @@
}
/* }}} */
+static inline void zend_init_type_casted_arg(zend_uchar op, int type_cast, zend_arg_info *cur_arg_info, const znode *initialization, zend_uchar pass_by_reference, char *default_error, char *reference_error) {
+ cur_arg_info->type_hint = type_cast;
+ 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) != (type_cast & 0x0F)) {
+ zend_error(E_COMPILE_ERROR, default_error);
+ }
+ }
+ if (pass_by_reference) {
+ zend_error(E_COMPILE_ERROR, reference_error);
+ }
+}
+
void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, const znode *initialization, znode *class_type, zend_uchar pass_by_reference TSRMLS_DC) /* {{{ */
{
zend_op *opline;
@@ -1880,8 +1894,8 @@
cur_arg_info->allow_null = 0;
if (class_type->u.constant.type != IS_NULL) {
- if (class_type->u.constant.type == IS_ARRAY) {
- cur_arg_info->type_hint = IS_ARRAY;
+ if (class_type->u.constant.type == IS_ARRAY || class_type->u.constant.type == IS_ARRAY_CAST) {
+ cur_arg_info->type_hint = class_type->u.constant.type;
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;
@@ -1889,6 +1903,59 @@
zend_error(E_COMPILE_ERROR, "Default value for parameters with array type hint can only be an array or NULL");
}
}
+ if (pass_by_reference && class_type->u.constant.type == IS_ARRAY_CAST) {
+ zend_error(E_COMPILE_ERROR, "Parameters with array cast type hint cannot be passed by reference");
+ }
+ } else if (class_type->u.constant.type == IS_LONG_CAST) {
+ zend_init_type_casted_arg(
+ op,
+ IS_LONG_CAST,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with int type hint can only be INT or NULL",
+ "Parameters with integer cast hint cannot be passed by reference"
+ );
+ } else if (class_type->u.constant.type == IS_DOUBLE_CAST) {
+ zend_init_type_casted_arg(
+ op,
+ IS_DOUBLE_CAST,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with float type hint can only be FLOAT or NULL",
+ "Parameters with float cast hint cannot be passed by reference"
+ );
+ } else if (class_type->u.constant.type == IS_BOOL_CAST) {
+ zend_init_type_casted_arg(
+ op,
+ IS_BOOL_CAST,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with boolean type hint can only be BOOL or NULL",
+ "Parameters with boolean cast hint cannot be passed by reference"
+ );
+ } else if (class_type->u.constant.type == IS_STRING_CAST) {
+ zend_init_type_casted_arg(
+ op,
+ IS_STRING_CAST,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with string type hint can only be STRING or NULL",
+ "Parameters with string cast hint cannot be passed by reference"
+ );
+ } else if (class_type->u.constant.type == IS_OBJECT_CAST) {
+ zend_init_type_casted_arg(
+ op,
+ IS_OBJECT_CAST,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with object type hint can only be NULL",
+ "Parameters with object cast hint cannot be passed by reference"
+ );
} else if (class_type->u.constant.type == IS_CALLABLE) {
cur_arg_info->type_hint = IS_CALLABLE;
if (op == ZEND_RECV_INIT) {
Index: Zend/zend_language_parser.y
===================================================================
--- Zend/zend_language_parser.y (revision 323850)
+++ Zend/zend_language_parser.y (working copy)
@@ -508,7 +508,6 @@
| /* empty */
;
-
non_empty_parameter_list:
optional_class_type T_VARIABLE { $$.op_type = IS_UNUSED; $$.u.op.num=1; zend_do_receive_arg(ZEND_RECV, &$2, &$$, NULL, &$1, 0 TSRMLS_CC); }
| optional_class_type '&' T_VARIABLE { $$.op_type = IS_UNUSED; $$.u.op.num=1; zend_do_receive_arg(ZEND_RECV, &$3, &$$, NULL, &$1, 1 TSRMLS_CC); }
@@ -525,10 +524,15 @@
/* empty */ { $$.op_type = IS_UNUSED; }
| T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; }
| T_CALLABLE { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_CALLABLE; }
+ | T_ARRAY_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY_CAST; }
+ | T_BOOL_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_BOOL_CAST; }
+ | T_INT_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_LONG_CAST; }
+ | T_DOUBLE_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_DOUBLE_CAST; }
+ | T_STRING_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_STRING_CAST; }
+ | T_OBJECT_CAST { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_OBJECT_CAST; }
| fully_qualified_class_name { $$ = $1; }
;
-
function_call_parameter_list:
non_empty_function_call_parameter_list { $$ = $1; }
| /* empty */ { Z_LVAL($$.u.constant) = 0; }
Index: Zend/zend_vm_def.h
===================================================================
--- Zend/zend_vm_def.h (revision 323850)
+++ Zend/zend_vm_def.h (working copy)
@@ -2676,7 +2676,7 @@
ulong arg_count = opline->extended_value;
while (arg_count>0) {
- zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 TSRMLS_CC);
+ zend_verify_arg_type(fbc, ++i, (p-arg_count), 0 TSRMLS_CC);
arg_count--;
}
}
@@ -3202,7 +3202,7 @@
} else {
zval **var_ptr;
- zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC);
+ zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, param, opline->extended_value TSRMLS_CC);
var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->result.var TSRMLS_CC);
Z_DELREF_PP(var_ptr);
*var_ptr = *param;
@@ -3238,7 +3238,7 @@
Z_ADDREF_P(assignment_value);
}
- zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC);
+ zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, &assignment_value, opline->extended_value TSRMLS_CC);
var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->result.var TSRMLS_CC);
Z_DELREF_PP(var_ptr);
*var_ptr = assignment_value;
Index: Zend/zend_vm_execute.h
===================================================================
--- Zend/zend_vm_execute.h (revision 323850)
+++ Zend/zend_vm_execute.h (working copy)
@@ -632,7 +632,7 @@
ulong arg_count = opline->extended_value;
while (arg_count>0) {
- zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 TSRMLS_CC);
+ zend_verify_arg_type(fbc, ++i, (p-arg_count), 0 TSRMLS_CC);
arg_count--;
}
}
@@ -782,7 +782,7 @@
} else {
zval **var_ptr;
- zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC);
+ zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, param, opline->extended_value TSRMLS_CC);
var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->result.var TSRMLS_CC);
Z_DELREF_PP(var_ptr);
*var_ptr = *param;
@@ -1366,7 +1366,7 @@
Z_ADDREF_P(assignment_value);
}
- zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC);
+ zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, &assignment_value, opline->extended_value TSRMLS_CC);
var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->result.var TSRMLS_CC);
Z_DELREF_PP(var_ptr);
*var_ptr = assignment_value;
Index: Zend/zend_API.c
===================================================================
--- Zend/zend_API.c (revision 323850)
+++ Zend/zend_API.c (working copy)
@@ -205,22 +205,36 @@
switch(type) {
case IS_BOOL:
return "boolean";
+ case IS_BOOL_CAST:
+ return "(bool)";
case IS_LONG:
return "integer";
+ case IS_LONG_CAST:
+ return "(int)";
case IS_DOUBLE:
return "double";
+ case IS_DOUBLE_CAST:
+ return "(float)";
case IS_STRING:
return "string";
+ case IS_STRING_CAST:
+ return "(string)";
case IS_OBJECT:
return "object";
+ case IS_OBJECT_CAST:
+ return "(object)";
case IS_RESOURCE:
return "resource";
case IS_NULL:
return "null";
+ case IS_NULL_CAST:
+ return "(unset)";
case IS_CALLABLE:
return "callable";
case IS_ARRAY:
return "array";
+ case IS_ARRAY_CAST:
+ return "(array)";
default:
return "unknown";
}
@ircmaxell
Copy link
Author

Note: updated patch to support null default parameters without casting to the destination type.

@LouisLandry
Copy link

👍

@marcioAlmada
Copy link

We so need a reboot of this RFC on PHP7 :)

@sergiors
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment