Step on a hole and pick a gold PHP data type that "broken" thing.

Source: Internet
Author: User


The journey of the boat is boundless.

PHP Learning side write almost more than a year, PHP, the weak type of language and the previous contact C, Java, AS3 and other languages are quite different, now feel very fortunate to start from C to learn programming, regardless of data type or pointer, at least there is a basic concept.

The PHP data type on a lot of pits, but also learned something, here to share, see the source code may be very boring, but understand some of the bottom-level implementation is good, don't step on the pit behind.

Order

before the online to see a hot Post said: PHP of the Ip2long have a Bug , please use it with caution. so I looked at the following description, roughly the following

<?php echo ip2long (' 58.99.11.1 '), "<br/>";   The output is 979569409 echo ip2long (' 58.99.011.1 '), "<br/>";  The output is 979568897 echo ip2long (' 058.99.11.1 '), "<br/>";  
look at the above seemingly "same" IP address, the output of the result "unexpectedly" is not the same. So the Post concludes:PHP 4.x,5.xin,with a leading zero.IPThe result of the conversion is not correct.

This goods really understand the programming language, really understand the data type?

The source code is not affixed, in the ext/standard/basic_functions.c file (5.3.28), simply call the C function Inet_pton or inet_addr, and then call Ntohl to convert the byte order. Needless to say, 011 has a leading 0 means 8 binary, so 011 becomes the decimal 9, So 58.99.11.1 and 58.99.011.1 is not the same, since it is 8, it is never possible to appear 8, so 058.99.11.1 is not legal, of course, there is no way to convert to long, written in the manual, invalid will return False,echo False, of course, is empty, but people are false~ so there is no bug.

Note: Ip2long for some IP in the + bit will overflow, so use sprintf generally ("%u",), just take a look.

First, Intval

The maximum value depends on the operating system. +bit system with the largest signedintegerRange is-2147483648to the2147483647. For example, on such a system,intval(' 1000000000000 ')will return2147483647. -on the bit system, the largest signedintegervalue is9223372036854775807.
The intval source is ultimately calledConvert_to_long_basefunction, simply paste part of the source code (ZEND/ZEND_OPERATORS.C):

           Switch (z_type_p (OP)) {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_bool:case is_long:break;case is_double:z_lval_p (OP) = Zend_dval_to_lval (Z_DVAL _p (OP)); Break;case Is_string:{char *strval = z_strval_p (OP); Z_lval_p (OP) = Strtol (Strval, NULL, base); 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;
You can see the results of various types of data transformations in a clearer view, with a focus on double and string. If the type is is_double using the Zend_dval_to_lval macro, the macro is defined in Zend _operators.h, and the main implication is
# define ZEND_DVAL_TO_LVAL (d) ((long) (d))
In fact, there are other branches of this macro, but the meaning is roughly the same, for long type has overflow double strong to long, the result is the same as in C, overflow.

If the type is is_string, call the C function strtol directly, this function is: if the integer value in the string exceeds the representation range (overflow or underflow) of longint , then Strtol Returns the largest (or smallest) integer that it can represent . So PHP's intval also have these behaviors.

Second, = =

Var_dump (In_array (0, Array (' s '))); Var_dump (0 = = "string"), Var_dump ("1111" = = "1112"); Var_dump ("111111111111111111" = "111111111111111112"); $str = ' String '; Var_dump ($str [' AAA ']); 32-bit bool (TRUE) bool (TRUE) bool (FALSE) bool (TRUE) string (1) "s" 64-bit BOOL (TRUE) bool (true) BOOL (FALSE) bool (FALSE) string (1) "s"
Here are some examples of the weak types of PHP that many people would cite, and I added 32-bit and 64-bit results.

First of all, each is basically based on the PHP comparison when the type conversion, is a relatively basic knowledge. Many people see these results will be a bit of emotion ~

Var_dump ("111111111111111111" = = "111111111111111112");
I am curious that the two strings compare why bit true, of course, in the 32-bit and 64-bit machine results are different, obviously related to the Rotary type, in the Internet did not see other people have explained, so search for the next source related. Roughly as follows:

= = This comparison operator, when comparing two strings, the core call method is zend_is_equal=>is_equal_function=>compare_function =zendi_smart_strcmp

And then put down the source of zendi_smart_strcmp , not very long

zend_api void zendi_smart_strcmp (Zval *result, Zval *s1, Zval *s2)/* {{*/{int ret1, ret2;l Ong Lval1, Lval2;double Dval1, Dval2;if ((Ret1=is_numeric_string (z_strval_p), S1 (z_strlen_p), S1, & Dval1, 0)) && (ret2=is_numeric_string (z_strval_p (S2), z_strlen_p (S2), &lval2, &dval2, 0)) {if (ret1== is_double) | | (ret2==is_double)) {if (ret1!=is_double) {dval1 = (double) Lval1;} else if (ret2!=is_double) {Dval2 = (double) Lval2;} else if (Dval1 = = Dval 2 &&!zend_finite (DVAL1)) {/* Both values overflowed and has the same sign, * So a numeric comparison would is in Accurate */goto string_cmp;} Z_dval_p (Result) = Dval1-dval2; Zval_long (Result, Zend_normalize_bool (z_dval_p (result)));} else {/* They both has to be long ' s */zval_long (result, Lval1 > Lval2? 1: (Lval1 < lval2? -1:0));}} else {string_cmp:z_lval_p (Result) = zend_binary_zval_strcmp (S1, S2); Zval_long (Result, Zend_normalize_bool (z_lval_p (result)));}} 

Where is_numeric_string is an inline function in zend_operators.h that determines whether a string is a number and returns a Is_long or is_double type, Where the decision is a LONG or double is the key point is the source of digits >= Max_length_of_long, then Max_length_of_long is what things?

This macro is defined in Zend.h

#if Sizeof_long = = 4#define max_length_of_long 11static const char long_min_digits[] = "2147483648"; #elif Sizeof_long = = 8 #define Max_length_of_long 20static const char long_min_digits[] = "9223372036854775808"; #else #error "Unknown sizeof_ LONG "#endif

Roughly understand, for the 32-bit machine long type is 4 bytes, 64-bit machine long is 8 bytes, the original difference here! Of course, it also has a predefined length of 11 and 202 I think is a magic number.

OK, the above that so many 1 of the string on the 32-bit machine is obviously is_double, and then there is a branch zend_finite determine whether it is a finite value, in fact, these are not very important now, the most important sentence is

Z_dval_p (Result) = Dval1-dval2; Zval_long (Result, Zend_normalize_bool (z_dval_p (result));
Where the Zend_normalize_bool macro is used to normalize the bool value

#define ZEND_NORMALIZE_BOOL (n) (n)? ((((n) >0)? 1:-1): 0)
Well, what exactly is the dval1-dval2, at this time to think of a double of the effective number of digits, C-type double the effective number of digits is about 16, the above string is 18 1, has exceeded the number of significant digits, do subtraction is not accurate, here do not want to go into the expression of double type, Simply display it in C language.

#include <stdio.h>int main () {Double A = 11111 11111 11111 12.0l;double b = 11111111111111111.0l;double c= 111111111 11111114.0l;printf ("%lf", A-B);p rintf ("%d", a-b = = 0);p rintf ("%lf", C-b);p rintf ("%d", c-b = = 0);}
For such a C program, the output result is
0.00000012.0000000
The 32-bit machine is the same as the 64-bit machine because the double type is 8 bytes.

Can try, Mantissa 1, 2, 3 subtraction are 0, to the mantissa of 4 will change, the results are not accurate, the following look at the memory of the expression:

Double c = 11111111111111111.0l;double d = 11111111111111112.0l;double E = 11111111111111113.0l;double F = 111111111111111 14.0l;double *p = &c;printf ("%x,%x\n", ((int *) p) [0], ((int *) p) [1]);p = &d;printf ("%x,%x\n", ((int *) p) [0], ( (int *) p) [1]);p = &e;printf ("%x,%x\n", ((int *) p) [0], ((int *) p) [1]);p = &f;printf ("%x,%x\n", ((int *) p) [0], ( (int *) p) [1]);
In fact, it is the double-type strong to the int array, and then the 16-input output, the result is:

936b38e4, 4343bcbf936b38e4, 4343bcbf936b38e4, 4343bcbf936b38e5, 4343BCBF
You can see that the mantissa is 4 not quite the same, combined above, that's why
Var_dump ("111111111111111111" = = "111111111111111112");
In the 32-bit machine The result is true, the 4-byte overflow is turned into a double, then the subtraction is inaccurate and becomes 0, resulting in equality. 64-bit machine is false because it does not overflow.


Third, Array_flip

On a 32-bit machine, use the enterprise QQ number to do associative array key, need to pay attention to more than 2.1 billion problems

32 bit $ A = Array (2355199999 = 1, 2355199998 = 1); Var_dump ($a); Array (2) {[-1939767297]=> int (1) [ -1939767298]=> ; Int (1)} $b = Array (2355199999, 2355199998), Var_dump ($b), Array (2) {[0]=> float (2355199999) [1]=> float (2355199998 )} var_dump (Array_flip ($b)); Warning:array_flip () Can only flip STRING and INTEGER values! $c = Array (), foreach ($b as $key + = $value) {    $c [$valu E] = $key;} Var_dump ($c);
Because key can only be a string or Interger, on a 32-bit machine, greater than 2.1 billion will become a float, so if you force float to do key, will overflow into a similar negative, etc. ~ here if the number greater than 2.1 billion plus quotation marks can be


Iv. Array_merge

To put it simply, array_merge in the document, if key is an integer, the merge key will be rearranged by the natural number

For example

<?php$a = Array (5 = 5, 7 = 4), $b = Array (1 = 1, 9 = 9); Var_dump (Array_merge ($a, $b));

The output is an array (4) {[0]=> int (5) [1]=> int (4) [2]=> int (1) [3]=> int (9)}

The implementation of the source code is relatively simple, I have seen, is encountered an integer on the use of Nextindex, encountered a string on the normal insert.

So on the 32-bit machine, if the key is greater than 2.1 billion, Array_merge will not use the key to turn the Nextindex into natural number rearrangement, on the 64-bit machine of course greater than 2.1 billion is no use ~

So if key is an integer, you can use Array+array instead when merging the array .

Array_merge ($a, $b) if the string key is the same, $b will overwrite $ A, if the key is a 32-bit or 64-bit long integer range, it will not overwrite, because the implementation is simple traversal overrides insert Hashtable.

Array+array If key is the same, keep the former and discard the latter.


Knot

I am glad that the first linguistics is the C language, although the undergraduate ignorant simple code to write a pretty slip, a variety of technical understanding is relatively small, but with C language and some C + + Foundation, study other languages or will be much easier to figure out some of the underlying implementation principle, of course, the underlying principle is to further study.




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.