A question about PHP type conversion
Published in my blog
Http://starlight36.com/post/php-type-convert
Recently, I have been interviewing new users for a company. one of the frequently asked questions is the PHP type conversion value. for example:
var_dump((int)true);var_dump((string)true);var_dump((string)false);var_dump((bool)"1");var_dump((bool)"0");var_dump((bool)"");var_dump((bool)"false");
I was impressed by the fact that this question was first mentioned in the pen questions of the British senior PHP development engineer. it seems very basic, but it still can be difficult to get a lot of PHPer. Let's take a look at the running results:
int(1)string(1) "1"string(0) ""bool(true)bool(false)bool(false)bool(true)
For most people, rows 1st, 2, and 4 are usually normal. But why is false converted to a string that is a null string? When processing the request value, a string type of false is usually passed, but "false" (string) is not false (Boolean), which is a bit confusing.
Why?
Let's start with the PHP kernel to see what happened inside the system during type conversion.
First, I would like to add some background knowledge about weak PHP implementation methods. The PHP interpreter is written in C language. of course, the final processing of the variable will also be implemented using the C language to construct the data structure. In the Zend Engine, the type of a PHP variable is zval.
Open the Zend/zend_types.h file, we can see the zval type definition, the php-5.5.23 version is around 55th lines:
typedef struct _zval_struct zval;
In this case, we found that zval is actually a struct type named _ zval_struct. we found the struct definition in the Zend/zend. h file, which starts around 320 rows:
typedef union _zvalue_value {long lval;/* long value */double dval;/* double value */struct {char *val;int len;} str;HashTable *ht;/* hash table value */zend_object_value obj;} zvalue_value;struct _zval_struct {/* Variable information */zvalue_value value;/* value */zend_uint refcount__gc;zend_uchar type;/* active type */zend_uchar is_ref__gc;};
As you can see, _ zval_struct contains two important members: zvalue_value and zend_uchar. Note that the zvalue_value type is a consortium used to store the value information of a PHP variable. (If you forget what a consortium is, let me explain it. A consortium is similar to a struct, but a member of a consortium can only have one storage space, and the space occupied by the consortium is the longest member in the consortium, this is done to save memory usage .) Zvalue_value contains five types of members: long, double, struct, HashTable, and zend_object_value. They are used to store values of different types of PHP variables:
C type |
PHP type |
Long |
Bool |
Int |
Resource |
Double |
Float |
Struct |
String |
HashTable |
Array |
Zend_object_value |
Object |
After seeing this struct, I must have understood the commonly asked questions such as the int type value range in PHP and the strlen time complexity in php.
It can be seen that PHP variable type conversion, or weak type implementation, is essentially a conversion between different types of zval. In addition to zvalue_value conversion, you also need to set the type in _ zval_struct to the type of the current variable. The convert_to _ * series functions are implemented in the Zend Engine to complete this conversion. we can see these conversion functions in Zend/zend_operators.c, which are about 511 rows, you can find the function to convert to a Boolean type:
ZEND_API void convert_to_boolean(zval *op) /* {{{ */{int tmp;switch (Z_TYPE_P(op)) {case IS_BOOL:break;case IS_NULL:Z_LVAL_P(op) = 0;break;case IS_RESOURCE: {TSRMLS_FETCH();zend_list_delete(Z_LVAL_P(op));}/* break missing intentionally */case IS_LONG:Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0);break;case IS_DOUBLE:Z_LVAL_P(op) = (Z_DVAL_P(op) ? 1 : 0);break;case IS_STRING:{char *strval = Z_STRVAL_P(op);if (Z_STRLEN_P(op) == 0|| (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {Z_LVAL_P(op) = 0;} else {Z_LVAL_P(op) = 1;}STR_FREE(strval);}break;case IS_ARRAY:tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);zval_dtor(op);Z_LVAL_P(op) = tmp;break;case IS_OBJECT:{zend_bool retval = 1;TSRMLS_FETCH();convert_object_to_type(op, IS_BOOL, convert_to_boolean);if (Z_TYPE_P(op) == IS_BOOL) {return;}zval_dtor(op);ZVAL_BOOL(op, retval);break;}default:zval_dtor(op);Z_LVAL_P(op) = 0;break;}Z_TYPE_P(op) = IS_BOOL;}/* }}} */
Case IS_STRING this code is to convert a string type variable to a Boolean type. It can be seen that only when the string is a null string or the string length is 1 and the character is 0, the boolean value of the string is 1, that is, true, and the other value is 0, that is, false.
Similarly, we understand how to convert a Boolean value to a string, which can be learned from the implementation of the _ convert_to_string function.
The root cause of seemingly simple and basic PHP problems is to grasp the PHP implementation mechanism. I personally think that this question is also a good question to identify the knowledge boundary of PHPer.