PHP 8.2: New ini_parse_quantity function

Version8.2
TypeNew Feature

PHP INI directives accept data sizes that can optionally contain a suffix to specify a unit multiplier, such as 5M for 5 Megabytes, or 1G for 1 Gigabyte. These suffixes are used widely in PHP INI files, but they are not international standardized suffixes.

ini_parse_quantity is a new function added in to PHP in PHP 8.2. ini_parse_quantity parses any data size recognized by PHP INI values (such as 56K, 256M, or 1G) and returns the data size in bytes. This function can be useful when parsing existing or provided INI values in a PHP application.

The ini_parse_quantity function does not recognize IEC suffixes for data sizes such as MB, MiB, or GB, and is not suitable to convert standard data size values to bytes.

For example, 1K is recognized in PHP INI values and ini_parse_quantity function as one Kibibyte, which is equal to 1024 bytes. ini_parse_quantity returns the parsed byte value following the exact approach PHP internally uses to parse INI values.

ini_parse_quantity function synopsis

function ini_parse_quantity(string $shorthand): int {
}

ini_parse_quantity function attempts to parse the given $shorthand value by inspecting its last character suffix, and if it matches one of the following recognized suffixes, the returned value is multiplied by the unit specified to calculate the return value.

The following suffixes are recognized:

Suffix Name Multiplier
k / K Kibibytes 1024
m / M Megabytes 1048576
g / G Gigabytes 1073741824

Note that this function emits warnings when it encounters a $shorthand value containing unrecognized suffixes (e.g 53Q), has no leading digits (R2D2), when the parsed byte value is underflown or overflown (9999999999G), or if the parser ignored unrecognized characters (12RG). It does not throw any exceptions, and the caller may need to inspect the emitted warning message to make sure the value was parsed correctly.

Further, this function does not recognize decimal values (1.5G).

ini_parse_quantity function (and PHP's internal INI value parsing) is designed to try and work around incorrectly formatted strings as opposed to being defensive and throwing an exception. If the function cannot parse a value at all, it returns 0.

Function Warning Messages

ini_parse_quantity function emits several types of warnings if it encounters a value that does not comply with the exact format it expects.

  • Values without leading digits return 0
    Values without a valid digit at the beginning, such as ABC, T5, S05e06, are invalid. They result in a warning similar to the following, and the function returns 0:
    Invalid quantity "S06e08": no valid leading digits, interpreting as "0" for backwards compatibility
  • Values without recognized suffix return the first found integer value
    Unit sizes not recognized by the function are ignored, and the absolute integer value contained in the passed value is returned.

    • 123R2D2 is parsed as "123"
    • 1234T is parsed as "123"
    • -1234T is parsed as "-123"

    Although an integer value is extracted, invalid values like this still result in a PHP warning:

    Invalid quantity "123R2D2": unknown multiplier "2", interpreting as "123" for backwards compatibility
  • Values with unrecognized characters in the middle return the quantified value with a warning
    If the passed value contains characters that were removed, a warning is emitted, but the function returns the parsed and quantified value.

    • 123FG is parsed as "123G" to 132070244352
    • 123$M is parsed as "123M" to 44040192
    • -123MM is parsed as "-123M" to -128974848

    Although an integer value is extracted, invalid values like this still result in a PHP warning:

    Invalid quantity "123R2D2": unknown multiplier "2", interpreting as "123" for backwards compatibility
  • Values that overflow/underflow return overflown/underflown value
    If the calculated value is not between the minimum and maximum integer size the CPU can handle, a warning is emitted, and the function returns the overflown/underflown value.

    • 9999999999G is calculated to -7709325834783293440
    • -9999999999G is calculated to 7709325834783293440

    The values above are overflown, but the function returns a value to be compatible with the INI parsing behavior. This condition is warned as well:

    Invalid quantity "-9999999999G": value is out of range, using overflow result for backwards compatibility

User-land PHP Implementation

The following is a user-land PHP implementation of the PHP 8.2 ini_parse_quantity function that should work on PHP 7.0 and later. It emits the same warnings as PHP 8.2 ini_parse_quantity function.

function ini_parse_quantity(string $shorthand): int {
    $original_shorthand = $shorthand;
    $multiplier = 1;
    $sign = '';
    $return_value = 0;

    $shorthand = trim($shorthand);

    // Return 0 for empty strings.
    if ($shorthand === '') {
        return 0;
    }

    // Accept + and - as the sign.
    if ($shorthand[0] === '-' || $shorthand[0] === '+') {
        if ($shorthand[0] === '-') {
            $multiplier = -1;
            $sign = '-';
        }
        $shorthand = substr($shorthand, 1);
    }

    // If there is no suffix, return the integer value with the sign.
    if (preg_match('/^\d+$/', $shorthand, $matches)) {
        return $multiplier * $matches[0];
    }

    // Return 0 with a warning if there are no leading digits
    if (preg_match('/^\d/', $shorthand) === 0) {
        trigger_error(sprintf('Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility', $original_shorthand), E_USER_WARNING);
        return $return_value;
    }

    // Removing whitespace characters.
    $shorthand = preg_replace('/\s/', '', $shorthand);

    $suffix = strtoupper(substr($shorthand, -1));
    switch ($suffix) {
        case 'K':
            $multiplier *= 1024;
            break;
        case 'M':
            $multiplier *= 1024 * 1024;
            break;
        case 'G':
            $multiplier *= 1024 * 1024 * 1024;
            break;
        default:
            preg_match('/\d+/', $shorthand, $matches);
            trigger_error(sprintf('Invalid quantity "%s": unknown multiplier "%s", interpreting as "%d" for backwards compatibility', $original_shorthand, $suffix, $sign . $matches[0]), E_USER_WARNING        );
            return $matches[0] * $multiplier;
    }

    $stripped_shorthand = preg_replace('/^(\d+)(\D.*)([kKmMgG])$/', '$1$3', $shorthand, -1, $count);
    if ($count > 0) {
        trigger_error(sprintf('Invalid quantity "%s", interpreting as "%s" for backwards compatibility', $original_shorthand, $sign . $stripped_shorthand), E_USER_WARNING);
    }

    preg_match('/\d+/', $shorthand, $matches);

    $multiplied = $matches[0] * $multiplier;
    if (is_float($multiplied)) {
        trigger_error(sprintf('Invalid quantity "%s": value is out of range, using overflow result for backwards compatibility', $original_shorthand), E_USER_WARNING);
    }

    return (int) ($matches[0] * $multiplier);
}

Backwards Compatibility Impact

ini_parse_quantity is a new PHP function added in PHP 8.2. This function is added with some internal code refactoring to the PHP engine, which causes PHP to emit warnings when parsing a provided INI file.

However, the ini_parse_quantity function can be trivially implemented in user-land PHP code in case an application or a library needs to make use of this function in an older PHP version.


Implementation