Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the problem of PHP large integers

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "how to solve the PHP big integer problem". In the daily operation, I believe many people have doubts about how to solve the PHP big integer problem. The editor consulted all kinds of data and sorted out a simple and easy-to-use operation method. I hope it will be helpful for you to answer the doubt of "how to solve the PHP big integer problem". Next, please follow the editor to study!

Problems encountered

Recently encountered a PHP large integer problem, the problem code is like this

$shopId = 17978812896666957068; var_dump ($shopId)

The above code output converts $shopId to float and represents it using scientific notation. The output is as follows:

Float (1.7978812896667E+19)

But what is needed in the program is a complete number as a parameter to find the data, so it needs to be a complete number. At that time, I thought it was just because the data was converted into scientific counting. So the solution is to force it not to use scientific counting:

$shopId= number_format (1797881289666666957068); var_dump ($shopId)

At this time, something strange happened, and the output was:

17978812896666957824

I didn't look at it carefully at that time, and I didn't look down after comparing the top ten, so I thought the problem was solved. When I really looked for the data according to ID, I found that the data could not be found out, and it was only then that I found that it was a data conversion error.

The reasons for the failure to use number_format will be discussed later, when I thought of converting the original data into a string, but it still doesn't work by using the following methods

$shopId= strval (17978812896666957068); var_dump ($shopId); $shopId= 17978812896666957068. ''; var_dump ($shopId)

The result of the output is

Float (1.7978812896667E+19)

* only the following options are feasible:

$shopId = '1797881289666666957068; var_dump ($shopId); / / output / / string (20) "17978812896666957068"

It is well known that PHP is an interpretive language, so I made a bold guess at the time that PHP converted the literal constants of numbers to float types during compilation and expressed them in scientific notation. But just guessing can't satisfy your curiosity. You only want to believe it if you want to see the real implementation code. So gradually analyze and explore until the realization behind it is found.

At the beginning, according to this question, I directly searched the Internet for "PHP large integer parsing process", but I didn't find the answer, so I had to track it down by myself. At the beginning, if you are not familiar with the execution process of PHP, the starting point is to debug step by step, and then

Sample code:

/ / test.php $var = 17978812896666957068; var_dump ($var)

Tracing process

1. View opcode

Looking at the opcode of the code executed by PHP through vld, you can see that the assignment is an opcode operation of ASSIGN

Next I want to see where the ASSIGN is executed.

2. Gdb debugging

2-1. Use list to see where breakpoints can be made.

2-2. I don't have a clue for the time being. Try it at 1186 breakpoint.

As a result, the program has reached line 1200 of the sapi/cli/php_cli.c file, press n to execute the next step, and here you have reached the output of the program:

2-4, so guess that the ASSIGN operation is carried out in the do_cli function, so do a breakpoint on the do_cli function: break do_cli.

Enter n, enter again and again, and go to the program output after line 993 of the sapi/cli/php_cli.c file:

2-5. Make a breakpoint on the php_execute_script function: break php_execute_script, execute step by step, and find that the output of the program will be reached on line 2537 of the main/main.c file:

2-6. Continue the breakpoint step: break zend_execute_scripts, repeat the previous step and find that 1476 of the zend/Zend.c file walks to the step of outputting the result of the program:

When you see this, there is an op_array in line 1475, and you wonder if it will already have a value at the time of op_array, so you start printing the value of op_array:

I didn't see any useful information after printing, but there was a lot of information here, such as opcode's handler: ZEND_ASSIGN_SPEC_CV_RETVAL_CV_CONST_RETVAL_UNUSED_HANDLER, but I didn't notice it at the time, so I wanted to see how op_array was assigned and what the steps had done.

2-7. Start again from the breakpoint of 2-5, and let the program execute step by step. You can see that the assignment of op_array is as follows:

Seeing that line 1470 assigns the result of running the zend_compile_file function to op_array, break zend_compile_file is told that zend_compile_file is not defined and traced to zend_compile_file through the source code tool points to compile_file, so break zend_compile

It is found that it is at the breakpoint of the Zend/zend_language_scanner.l file, step by step, and when you see this line of pass_two (op_array), guess it may have a value here, so print it:

The result is still the same as before, but at this time you see a value of opcodes, and then print it.

If you see opcode = 38, if you look up 38 on the Internet, it means assignment.

2-8. So you can know that the opcode of ASSIGN has been obtained before this step, so keep looking forward, starting from the initialization of op_array, gradually printing the value of op_array- > opcodes, which has always been null.

The value of opcode = 38 is not obtained until CG (zend_lineno) = last_lineno; is executed:

Because this sentence: CG (zend_lineno) = last_lineno; is a macro, so there is no clue, close to giving up state.

So first to understand the data structure of opcode, in an in-depth understanding of the PHP kernel book to find the opcode processing function search this chapter, gave me some ideas to continue.

Quote the contents:

There is a function inside PHP to quickly return the pointer to the opcode handler corresponding to a specific opcode: the zend_vm_get_opcode_handler () function:

We know that in fact, the naming of opcode processing functions has the following rules

ZEND_ [opcode] _ SPEC_ (variable type 1) _ (variable type 2) _ HANDLER

Based on what was debugged before, you see a value of handler at 2-6:

Yes

ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER

Find out the definition of the function as follows:

As you can see, during the opcode operation, the value is obtained from EX_CONSTANT, and the macro is expanded according to the definition, that is

Opline- > op2- > execute_data- > literals

Here are two pieces of information:

The parameter conversion is done before opcode is executed.

The assignment process is based on op2- > execute_data- > literals. If the guess is correct, op2- > execute_data- > literals saves the value after format conversion, which can be printed and verified.

The print result is as follows:

The conjecture was verified correctly, but I didn't see the real place to do the conversion, so I didn't give up and continued to find the bottom layer of PHP's Zend to do the compiled logic code.

Refer to the open source GitHub project. The PHP compilation phase is shown below:

Guess that the conversion is most likely to be done in the zendparse and zend_compile_top_stmt phases, because what these two phases do is convert the PHP code into an opcode array.

I searched the Internet for articles related to PHP parsing, and one of them talked about the process of parsing integers, so I found where PHP really converts large integers:

{LNUM} {char * end; if (yyleng < MAX_LENGTH_OF_LONG-1) {/ * Won't overflow * / errno = 0; ZVAL_LONG (zendlval, ZEND_STRTOL (yytext, & end, 0)) / * This isn't an assert, we need to ensure 019 isn't valid octal * Because the lexing itself doesn't do that for us * / if (end! = yytext + yyleng) {zend_throw_exception (zend_ce_parse_error, "Invalid numeric literal", 0); ZVAL_UNDEF (zendlval); RETURN_TOKEN (T_LNUMBER);} else {errno = 0 ZVAL_LONG (zendlval, ZEND_STRTOL (yytext, & end, 0); if (errno = = ERANGE) {/ * Overflow * / errno = 0; if (yytext [0] = ='0') {/ * octal overflow * / ZVAL_DOUBLE (zendlval, zend_oct_strtod (yytext, (const char * *) & end)) } else {ZVAL_DOUBLE (zendlval, zend_strtod (yytext, (const char * *) & end);} / * Also not an assert for the same reason * / if (end! = yytext + yyleng) {zend_throw_exception (zend_ce_parse_error, "Invalid numeric literal", 0); ZVAL_UNDEF (zendlval) RETURN_TOKEN (T_DNUMBER);} RETURN_TOKEN (T_DNUMBER);} / * Also not an assert for the same reason * / if (end! = yytext + yyleng) {zend_throw_exception (zend_ce_parse_error, "Invalid numeric literal", 0); ZVAL_UNDEF (zendlval); RETURN_TOKEN (T_DNUMBER) }} ZEND_ASSERT (! errno); RETURN_TOKEN (T_LNUMBER);}

As you can see, when the zend engine does lexical analysis on the expression of pure numbers in PHP code, it first determines whether the number may overflow, if it is possible to overflow, first try to save it with LONG type, if overflow, first convert it to double type with zend_strtod, and then save it with the zval structure of double type.

Reasons for the failure of number_format

Through gdb debugging, the number_format function is traced, and the php_conv_fp function is finally called at the bottom of the PHP to convert the numbers:

The function prototype is as follows:

PHPAPI char * php_conv_fp (register char format, register double num, boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char * buf, size_t * len)

The parameter num received here is of type double, so if a string type number is passed in, the number_format function will also pass it into the php_conf_fp function as a double type. The reason why this double type of num is finally output to 17978812896666957824 is that the accuracy after scientific counting is lost, and the original value cannot be restored when it is converted to double. Verify in C language:

Double local_dval = 1.7978812896666958 printf ("% f\ n", local_dval)

The result of the output is

17978812896666957824.000000

So, this is not PHP's bug, that's what it is.

Solution to this kind of problem

For storage, pure integers beyond the range represented by PHP*** can be saved using bigint/varchar in MySQL, and MySQL will save them using the string type when querying them.

For assignment, in PHP, if you encounter a large integer that needs to be assigned, do not try to assign a value with an integer type, for example, do not use the following:

$var = 17978812896666957068

And use this:

$var = '17978812896666957068'

For number_format, the recommended value is 9007199254740991 for which the precision of parsing will not be lost under 64-bit operating systems.

At this point, the study on "how to solve the PHP big integer problem" is over. I hope to be able to solve everyone's doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report