Add `wp_ini_parse_quantity()` to report numeric php.ini directive values
This PR for discussion of code and tests - see companion ticket
Trac ticket: https://core.trac.wordpress.org/ticket/55635
Resolves #17725 (https://core.trac.wordpress.org/ticket/17725)
When wp_convert_hr_to_bytes() was introduced in [4388] it provided a simplified mechanism to parse the values returned by functions like ini_get() which represent byte sizes. The over-simplified approach has led to issues in that function reporting the wrong byte sizes for various php.ini directives, leading to confusing problems such as uploading files that are rejected improperly or accepted improperly.
In this patch we're porting the parser from PHP's own source (which has
remained stable for decades and probably can't change without major breakage)
in order to more accurately reflect the values it uses when it reads those
configurations. This is available in the new function wp_ini_parse_quantity() found
inside of wp-includes/php-compat.php and loaded automatically in load.php.
Unfortunately PHP doesn't offer a mechanism to read its own internal value for these fields and a 100% port is extremely cumbersome (at best) due to the different ways that PHP and C handle signed integer overflow. These differences should only appear when supplying discouraged/invalid values to the system anyway, and PHP warns that in these situations things are likely to break anyway.
Sentinel overlap
There is semantic overlap between two values:
- Impose no limit on the memory use.
- Allow infinite memory use.
These behave differently under comparison, because imposing no limit on memory use implies that any other actual limit takes precedence over it. Some limit is more restrictive than no limit.
But then when examining unlimited use, this restriction is more liberal than any other memory limit than itself. It will always return as not-less-than any other limit.
There lacks a clear way to disambiguate these.
Testing
Since initially proposing this, PHP updated its own internal parser and now issues warnings for invalid values. It also sets a default value in cases where the value fails to parse.
| PHP version | memory_limit |
allowed memory size | crashed when… | note |
|---|---|---|---|---|
| 8.5.1 | -1 |
killed after allocating 210 GB… | ||
| 8.5.1 | 0 |
134,217,728 (128 MB) | immediate crash | PHP Warning: Failed to set memory limit to 0 bytes (Current memory usage is 2097152 bytes) in Unknown on line 0 |
| 8.5.1 | 1 |
134,217,728 (128 MB) | immediate crash | PHP Warning: Failed to set memory limit to 1 bytes (Current memory usage is 2097152 bytes) in Unknown on line 0 |
| 8.5.1 | 1G |
1,073,741,824 (1 GB) | after 29 doublings | |
| 8.5.1 | ship |
134,217,728 (128 MB) | after 26 doublings | PHP Warning: Invalid "memory_limit" setting. Invalid quantity "ship": no valid leading digits, interpreting as "0" for backwards compatibility in Unknown on line 0 |
| 8.5.1 | 1e5 |
134,217,728 (128 MB) | immediately | PHP Warning: Invalid "memory_limit" setting. Invalid quantity "1e5": unknown multiplier "5", interpreting as "1" for backwards compatibility in Unknown on line 0 |
| 8.5.1 | 9223372036854775807g |
-1,073,741,824 (-1 GB) | immediately | PHP Warning: Invalid "memory_limit" setting. Invalid quantity "9223372036854775807g": value is out of range, using overflow result for backwards compatibility in Unknown on line 0 |
Reported value from internal PHP PG(memory_limit) or ini_parse_quantity()
memory_limit |
7.4.33 | 8.5.1 |
|---|---|---|
| -1000 | -1000 | 134217728 |
| -1 | -1 | -1 |
| - | 0 | 134217728 |
| 1 | 1 | 134217728 |
| 1G | 1073741824 | 1073741824 |
| ship | 0 | 134217728 |
| 1e5 | 1 | 134217728 |
| 9223372036854775807 | 9223372036854775807 | 9223372036854775807 |
| 9223372036854775808 | 9223372036854775807 | -9223372036854775808 |
| 9223372036854775807g | -1073741824 | -1073741824 |