Skip to content

Commit a615828

Browse files
committed
Add ini_bytes to translate ini directive byte shorthand to a count
With WordPress we have seen a variety of bugs that stem from the fact that we don't reliably parse the memory size values from php.ini directives. We want to know those values in order to communicate to site users what the file upload limits are. In this patch I'm adding a new function `ini_bytes( $shorthand )` to return whatever PHP evaluates that value to be internally, thus exposing the actual byte size to the outside world. This eliminates the need for WordPress and other projects to port the config value parser since they could directly call it. Additionally it will reliably reproduce any quirks due to the platform build and overflow behaviors when handling the `g`, `m`, and `k` suffixes on `post_max_size` and `upload_max_filesize` (and others). This function is a shallow wrapper around `zend_atoi()` and could become out of sync with the actual ini directive parsing. I considered a variation of this function which would take a directive's name and return the stored internal value directly, keeping it in sync. ```php switch ( param_name ) { case 'post_max_size': RETVAL_LONG(SG(post_max_size)); } ``` After looking briefly at this option I dismissed it for being a bit too coupled to those options, too demanding to add all the possible config options to a big switch statement, and too complicated for returning all of the possible return types stored in the SAPI globals. This is my first attempt at patching PHP and I don't know what I'm doing :) I'm not thrilled at the function name but I needed something. I'm not aware of what I need to write for testing, documentation, etc... but I'm happy to add whatever is required. The expecation is to use it in situations roughly like this: ```php function max_upload_size() { $u_bytes = ini_bytes( ini_get( 'upload_max_filesize' ) || 0 ); $p_bytes = ini_bytes( ini_get( 'post_max_size' ) || 0 ); return min( $u_bytes, $p_bytes ); } ``` This patch also makes it easier to perform mathematic comparisons and operations on the config values, which again previously required duplicating the parsing outside of PHP and likely will fail for a variety of config values. ```php function validate_upload_size() { $u_bytes = ini_bytes( ini_get( 'upload_max_filesize' ) || 0 ); $p_bytes = ini_bytes( ini_get( 'post_max_size' ) || 0 ); return $u_bytes <= $p_bytes ? [ true ] : [ false, 'upload_max_filesize must be smaller than post_max_size' ]; } ```
1 parent 3648610 commit a615828

File tree

3 files changed

+22
-0
lines changed

3 files changed

+22
-0
lines changed

ext/standard/basic_functions.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,20 @@ PHP_FUNCTION(highlight_string)
19691969
}
19701970
/* }}} */
19711971

1972+
/* {{{ Get a byte size from the ini byte size shorthand */
1973+
PHP_FUNCTION(ini_bytes)
1974+
{
1975+
char *shorthand;
1976+
size_t shorthand_len;
1977+
1978+
ZEND_PARSE_PARAMETERS_START(1, 1)
1979+
Z_PARAM_STRING(shorthand, shorthand_len)
1980+
ZEND_PARSE_PARAMETERS_END();
1981+
1982+
RETVAL_LONG(zend_atol(shorthand, shorthand_len));
1983+
}
1984+
/* }}} */
1985+
19721986
/* {{{ Get a configuration option */
19731987
PHP_FUNCTION(ini_get)
19741988
{

ext/standard/basic_functions.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ function ini_alter(string $option, string|int|float|bool|null $value): string|fa
493493

494494
function ini_restore(string $option): void {}
495495

496+
function ini_bytes(string $option): int {}
497+
496498
/** @refcount 1 */
497499
function set_include_path(string $include_path): string|false {}
498500

ext/standard/basic_functions_arginfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ini_get, 0, 1, MAY_BE_STRING|MAY
484484
ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
485485
ZEND_END_ARG_INFO()
486486

487+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ini_bytes, 0, 1, MAY_BE_LONG)
488+
ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
489+
ZEND_END_ARG_INFO()
490+
487491
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ini_get_all, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
488492
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, extension, IS_STRING, 1, "null")
489493
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, details, _IS_BOOL, 0, "true")
@@ -2354,6 +2358,7 @@ ZEND_FUNCTION(highlight_file);
23542358
ZEND_FUNCTION(php_strip_whitespace);
23552359
ZEND_FUNCTION(highlight_string);
23562360
ZEND_FUNCTION(ini_get);
2361+
ZEND_FUNCTION(ini_bytes);
23572362
ZEND_FUNCTION(ini_get_all);
23582363
ZEND_FUNCTION(ini_set);
23592364
ZEND_FUNCTION(ini_restore);
@@ -2985,6 +2990,7 @@ static const zend_function_entry ext_functions[] = {
29852990
ZEND_FE(php_strip_whitespace, arginfo_php_strip_whitespace)
29862991
ZEND_FE(highlight_string, arginfo_highlight_string)
29872992
ZEND_FE(ini_get, arginfo_ini_get)
2993+
ZEND_FE(ini_bytes, arginfo_ini_bytes)
29882994
ZEND_FE(ini_get_all, arginfo_ini_get_all)
29892995
ZEND_FE(ini_set, arginfo_ini_set)
29902996
ZEND_FALIAS(ini_alter, ini_set, arginfo_ini_alter)

0 commit comments

Comments
 (0)