Implementation of type hints
PHP is a weakly typed language, and data types are not strictly checked when passing parameters to methods. However, sometimes it is necessary to determine the parameters passed into the method. For this reason, PHP provides some functions to determine the type of data. For example, is_numeric() determines whether it is a numeric value or a string that can be converted into a numeric value, such as the type operator used to determine the object: instanceof. instanceof is used to determine whether a given object is from a specified object class. The instanceof operator was introduced in PHP 5. Before this, is_a() was used, but its use is no longer recommended.
In order to avoid problems caused by non-standard object types, the concept of type hints was introduced in PHP5. When defining method parameters, also define the object type of the parameters. If the type of the incoming parameter does not match the defined parameter type when calling, an error will be reported. In this way, the type of objects can be filtered, or the security of the data can be guaranteed.
The type hint function in PHP can only be used for hints whose parameters are objects, but cannot be used for type hints such as integers, strings, floating point, etc. After PHP5.1, PHP supports type hints for arrays.
To use type hints, just add the name of an existing class before the object type parameter of a method (or function). When using type hints, you can not only specify the object type, but also abstract classes and interfaces.
Type hint example for an array:
function array_print(Array $arr) {
print_r($arr);}
array_print(1);
There is a problem with the above code. It triggers the type hint we introduced this time. When this code is executed in versions after PHP5.1, the following error will be reported:
Catchable fatal error: Argument 1 passed to array_print() must be an array,
integer given, called in ...
When we change the integer variable in the function parameter into an array, the program will run normally and call the print_r function to output the array. So how is this type hint implemented? Whether it is a method in a class or a function we call, the function keyword is used as a mark of its declaration. The implementation of type hints is related to the declaration of the function. The type of the parameter is already determined at the time of declaration. Which, but will only be displayed when called. Here, we explain the implementation of type hints from two aspects:
Type hints when declaring parameters
Type hints when calling functions or methods
Modify the example just now:
function array_print(Array $arr = 1) {
print_r($arr);}
array_print(array(1));
Compared with the previous example, this code sets a default value for the function parameter, but this default value is an integer variable, which is different from the type hint Array given by the parameter. Therefore, when we run this When you snippet the code, you will quickly see that the program will report an error as follows:
Fatal error: Default value for parameters with array type hint
can only be an array or NULL
Why do you see the error so quickly? Because the detection process of the default value occurs in the intermediate code generation stage, unlike the error reporting at runtime, it has not yet generated the intermediate code, and there is no process of executing the intermediate code. In the Zend/zend_language_parser.y file, we find that the parameter list of the function will call the zend_do_receive_arg function at compile time. In the parameter list of this function, the fifth parameter (znode *class_type) is closely related to the type hint we want to describe in this section. The function of this parameter is to declare the type in the type hint. There are three types here:
Empty, that is, there is no type hint
Class name, user-defined or PHP-customized classes, interfaces, etc.
array, the corresponding token during compilation is T_ARRAY, which is Array string
In the zend_do_receive_arg function, a series of operations are performed on the class_type parameter, basically for the three types listed above. For the class name, the program does not determine whether the class exists, even if you use a different For existing class names, when the program reports an error, it will also be displayed that the object given by the actual parameter is not an instance of the given class.
The above is the process of declaring type hints and the process of judging the default value of parameters during the declaration process. Let's look at the implementation of type hints when calling functions or methods.
From the above declaration process, we know that PHP calls the zend_do_receive_arg function in the Zend/zend_complie.c file when compiling the code related to type hints. In this function, the opcode of the type hint judgment is assigned the value ZEND_RECV. According to the mapping calculation rules of opcode, it is concluded that ZEND_RECV_SPEC_HANDLER is called during execution. The code is as follows:
static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS){
...//Omit
if (param == NULL) {
char *space;
char *class_name = get_active_class_name(&space TSRMLS_CC);
zend_execute_data *ptr = EX(prev_execute_data);
if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) {
…//Omit
}
…//Omit
} else {
…//Omit
zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC);
…//Omit
}
...//omitted}
As shown above: the last call in ZEND_RECV_SPEC_HANDLER is zend_verify_arg_type. The code is as follows:
static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC){
...//Omit
if (cur_arg_info->class_name) {
const char *class_name;
if (!arg) {
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, "none", "" TSRMLS_CC);
}
if (Z_TYPE_P(arg) == IS_OBJECT) { // Since it is a class object parameter, the passed parameter needs to be an object type
// Next check whether this object is an instance object of the parameter prompt class. Here, it is allowed to pass subclass instance objects
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(zf, arg_num, cur_arg_info, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);
}
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { // The parameter is NULL, which can also pass the check,
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) { // Array
if (!arg) {
;
}
if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) {
;
}
}
return 1;}
The entire process of zend_verify_arg_type is shown in Figure 3.1:
Figure 3.1 Type prompt judgment flow chart
If the type hint reports an error, the zend_verify_arg_type function will finally call zend_verify_arg_class_kind to generate the error message, and call zend_verify_arg_error to report the error. The code is as follows:
static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC){
*pce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info->class_name_len, (fetch_type | ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC);
*class_name = (*pce) ? (*pce)->name: cur_arg_info->class_name;
if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) {
return "implement interface ";
} else {
return "be an instance of ";
}}
static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const 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, "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, "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;}
在上面的代码中,我们可以找到前面的报错信息中的一些关键字Argument、 passed to、called in等。 这就是我们在调用函数或方法时类型提示显示错误信息的最终执行位置。
http://www.bkjia.com/PHPjc/963684.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/963684.htmlTechArticle类型提示的实现 PHP是弱类型语言,向方法传递参数时候也并不严格检查数据类型。 不过有时需要判断传递到方法中的参数,为此PHP中提供...