Skip to content

Instantly share code, notes, and snippets.

@ircmaxell
Created March 9, 2012 02:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ircmaxell/2004650 to your computer and use it in GitHub Desktop.
Save ircmaxell/2004650 to your computer and use it in GitHub Desktop.
Scalar Type Hint Patch, Version 3 - no generated files
Index: Zend/zend.h
===================================================================
--- Zend/zend.h (revision 323850)
+++ Zend/zend.h (working copy)
@@ -575,6 +575,9 @@
#define IS_CONSTANT_ARRAY 9
#define IS_CALLABLE 10
+/* Ugly hack to keep track of string hints vs class names */
+#define IS_STRING_HINT 0x026
+
/* 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,11 @@
return 0;
}
-static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC)
+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 +612,123 @@
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:
+ switch (Z_TYPE_PP(arg)) {
+ case IS_DOUBLE:
+ break;
+ case IS_STRING:
+ {
+ double *d;
+ long *p;
+ if (0 == is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), p, d, -1)) {
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type float", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ }
+ case IS_NULL:
+ case IS_LONG:
+ case IS_BOOL:
+ SEPARATE_ZVAL_IF_NOT_REF(arg);
+ convert_to_double(*arg);
+ break;
+ case IS_ARRAY:
+ case IS_OBJECT:
+ case IS_RESOURCE:
+ default:
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type float", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ break;
+ case IS_BOOL:
+ switch (Z_TYPE_PP(arg)) {
+ case IS_BOOL:
+ break;
+ case IS_STRING:
+ case IS_DOUBLE:
+ case IS_NULL:
+ case IS_LONG:
+ SEPARATE_ZVAL_IF_NOT_REF(arg);
+ convert_to_boolean(*arg);
+ break;
+ case IS_ARRAY:
+ case IS_OBJECT:
+ case IS_RESOURCE:
+ default:
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type boolean", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ break;
+ case IS_STRING:
+ switch (Z_TYPE_PP(arg)) {
+ case IS_STRING:
+ break;
+ case IS_DOUBLE:
+ case IS_NULL:
+ case IS_LONG:
+ case IS_BOOL:
+ SEPARATE_ZVAL_IF_NOT_REF(arg);
+ convert_to_string(*arg);
+ break;
+ case IS_OBJECT:
+ {
+ char **p;
+ int *pl;
+ SEPARATE_ZVAL_IF_NOT_REF(arg);
+ if (zend_parse_arg_object_to_string(arg, p, pl, IS_STRING TSRMLS_CC) == SUCCESS) {
+ break;
+ }
+ }
+ case IS_ARRAY:
+ case IS_RESOURCE:
+ default:
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type string", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ break;
+ case IS_LONG:
+ switch (Z_TYPE_PP(arg)) {
+ case IS_LONG:
+ break;
+ case IS_STRING:
+ {
+ double *d;
+ long *p;
+ if (0 == is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), p, d, -1)) {
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type int", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ }
+ case IS_DOUBLE:
+ case IS_NULL:
+ case IS_BOOL:
+ SEPARATE_ZVAL_IF_NOT_REF(arg);
+ convert_to_long(*arg);
+ break;
+ case IS_ARRAY:
+ case IS_OBJECT:
+ case IS_RESOURCE:
+ default:
+ return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type int", "", zend_zval_type_name(*arg), "" TSRMLS_CC);
+ }
+ break;
default:
zend_error(E_ERROR, "Unknown typehint");
}
Index: Zend/zend_language_scanner_defs.h
===================================================================
--- Zend/zend_language_scanner_defs.h (revision 323850)
+++ Zend/zend_language_scanner_defs.h (working copy)
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.5 on Thu Mar 1 21:30:38 2012 */
+/* Generated by re2c 0.13.5 on Thu Mar 8 20:29:09 2012 */
#line 3 "Zend/zend_language_scanner_defs.h"
enum YYCONDTYPE {
Index: Zend/zend_compile.c
===================================================================
--- Zend/zend_compile.c (revision 323850)
+++ Zend/zend_compile.c (working copy)
@@ -1822,6 +1822,17 @@
}
/* }}} */
+static inline void zend_init_type_casted_arg(zend_uchar op, int type, zend_arg_info *cur_arg_info, const znode *initialization, zend_uchar pass_by_reference, char *default_error) {
+ cur_arg_info->type_hint = 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;
+ } else if (Z_TYPE(initialization->u.constant) != type) {
+ zend_error(E_COMPILE_ERROR, default_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;
@@ -1881,7 +1892,7 @@
if (class_type->u.constant.type != IS_NULL) {
if (class_type->u.constant.type == IS_ARRAY) {
- cur_arg_info->type_hint = IS_ARRAY;
+ 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 +1900,42 @@
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_LONG) {
+ zend_init_type_casted_arg(
+ op,
+ IS_LONG,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with int type hint can only be INT or NULL"
+ );
+ } else if (class_type->u.constant.type == IS_DOUBLE) {
+ zend_init_type_casted_arg(
+ op,
+ IS_DOUBLE,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with float type hint can only be FLOAT or NULL"
+ );
+ } else if (class_type->u.constant.type == IS_BOOL) {
+ zend_init_type_casted_arg(
+ op,
+ IS_BOOL,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with boolean type hint can only be BOOL or NULL"
+ );
+ } else if (class_type->u.constant.type == IS_STRING_HINT) {
+ zend_init_type_casted_arg(
+ op,
+ IS_STRING,
+ cur_arg_info,
+ initialization,
+ pass_by_reference,
+ "Default value for parameters with string type hint can only be STRING or NULL"
+ );
} 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)
@@ -164,6 +164,10 @@
%token T_USE "use (T_USE)"
%token T_INSTEADOF "insteadof (T_INSTEADOF)"
%token T_GLOBAL "global (T_GLOBAL)"
+%token T_TYPE_BOOL "bool (T_TYPE_BOOL)"
+%token T_TYPE_INT "int (T_TYPE_INT)"
+%token T_TYPE_FLOAT "float (T_TYPE_FLOAT)"
+%token T_TYPE_STRING "string (T_TYPE_STRING)"
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
%token T_STATIC "static (T_STATIC)"
%token T_ABSTRACT "abstract (T_ABSTRACT)"
@@ -508,7 +512,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 +528,13 @@
/* 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_TYPE_BOOL { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_BOOL; }
+ | T_TYPE_INT { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_LONG; }
+ | T_TYPE_FLOAT { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_DOUBLE; }
+ | T_TYPE_STRING { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_STRING_HINT; }
| 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_language_scanner.l
===================================================================
--- Zend/zend_language_scanner.l (revision 323850)
+++ Zend/zend_language_scanner.l (working copy)
@@ -1324,6 +1324,22 @@
return T_CALLABLE;
}
+<ST_IN_SCRIPTING>"bool" {
+ return T_TYPE_BOOL;
+}
+
+<ST_IN_SCRIPTING>"int" {
+ return T_TYPE_INT;
+}
+
+<ST_IN_SCRIPTING>"float" {
+ return T_TYPE_FLOAT;
+}
+
+<ST_IN_SCRIPTING>"string" {
+ return T_TYPE_STRING;
+}
+
<ST_IN_SCRIPTING>"++" {
return T_INC;
}
Index: Zend/zend_API.c
===================================================================
--- Zend/zend_API.c (revision 323850)
+++ Zend/zend_API.c (working copy)
@@ -259,7 +259,7 @@
}
/* }}} */
-static int parse_arg_object_to_string(zval **arg, char **p, int *pl, int type TSRMLS_DC) /* {{{ */
+ZEND_API int zend_parse_arg_object_to_string(zval **arg, char **p, int *pl, int type TSRMLS_DC) /* {{{ */
{
if (Z_OBJ_HANDLER_PP(arg, cast_object)) {
zval *obj;
@@ -445,7 +445,7 @@
break;
case IS_OBJECT:
- if (parse_arg_object_to_string(arg, p, pl, IS_STRING TSRMLS_CC) == SUCCESS) {
+ if (zend_parse_arg_object_to_string(arg, p, pl, IS_STRING TSRMLS_CC) == SUCCESS) {
if (c == 'p' && CHECK_ZVAL_NULL_PATH(*arg)) {
return "a valid path";
}
Index: Zend/zend_API.h
===================================================================
--- Zend/zend_API.h (revision 323850)
+++ Zend/zend_API.h (working copy)
@@ -246,6 +246,8 @@
/* Parameter parsing API -- andrei */
#define ZEND_PARSE_PARAMS_QUIET 1<<1
+
+ZEND_API int zend_parse_arg_object_to_string(zval **arg, char **p, int *pl, int type TSRMLS_DC);
ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, const char *type_spec, ...);
ZEND_API int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, const char *type_spec, ...);
ZEND_API char *zend_zval_type_name(const zval *arg);
@cataphract
Copy link

Just a note: you can use convert_to_XXX_ex instead of convert_to_ex to also do the separation and skip the conversion if the type is already the desired.

@ircmaxell
Copy link
Author

@cataphract: Sure. However, for a final patch I'd like to refactor ZPP and this to have a shared implementation of the type conversion for the base types (int, float, bool, string at least). That way the are literally the same casting rules. I just did a quick copy-paste from ZPP to get these rules working, and we can refactor (or decide how to refactor best) later...

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