| 1428 | | * Converts a shorthand byte value to an integer byte value. |
| | 1428 | * Returns byte size represented in a numeric php.ini directive. |
| | 1429 | * |
| | 1430 | * Directives in php.ini may use a string representation of a number of bytes |
| | 1431 | * or a "shorthand" byte size to reference larger values. Multiple numeric |
| | 1432 | * php.ini directive use these shorthands even when they don't refer bytes. |
| | 1433 | * |
| | 1434 | * Example: |
| | 1435 | * |
| | 1436 | * wp_convert_hr_to_bytes( "1m" ) == 1048576 |
| | 1437 | * wp_convert_hr_to_bytes( "2K" ) == 2048 // 2 * 1024 |
| | 1438 | * wp_convert_hr_to_bytes( "0.5g" ) == 0 |
| | 1439 | * wp_convert_hr_to_bytes( "14.6e-13g" ) == 15032385536 // 14 * 1024^3 |
| | 1440 | * wp_convert_hr_to_bytes( "-813k" ) == 0; |
| | 1441 | * wp_convert_hr_to_bytes( "boat" ) == 0; |
| | 1442 | * |
| | 1443 | * // This gives an answer, but it's _wrong_ because |
| | 1444 | * // the underlying mechanism in PHP overflowed and |
| | 1445 | * // the real return value depends on whether PHP |
| | 1446 | * // was built with 64-bit support. |
| | 1447 | * wp_convert_hr_to_bytes( "9223372036854775807g" ) == ?? |
| | 1448 | * |
| | 1449 | * Notes: |
| | 1450 | * - Suffix units are case-insensitive and are always determined |
| | 1451 | * by looking at the last character in the input string. |
| | 1452 | * - Suffix units k/m/g report powers of 1024. PHP and the IEC disagree |
| | 1453 | * on the meaning of "kilobyte," "megabyte," and "gigabyte." |
| | 1454 | * - This function will not fail; it stops parsing after finding |
| | 1455 | * the last consecutive digit at the front of the trimmed string. |
| | 1456 | * - Invalid string representations return a value of 0. |
| | 1457 | * - This code mirrors the computation inside PHP and should only |
| | 1458 | * change its output if PHP redefines how it parses numeric php.ini |
| | 1459 | * directive. If you think something is wrong or should be |
| | 1460 | * refactored it probably shouldn't be. Consult `zend_operators.c` |
| | 1461 | * in the PHP-SRC repository for more info; specifically |
| | 1462 | * `zend_atol()` and `_is_numeric_string_ex()`. |
| | 1463 | * - As noted in the PHP documentation, any numeric value that overflows |
| | 1464 | * an integer for the platform on which PHP is built will break. |
| 1443 | | if ( false !== strpos( $value, 'g' ) ) { |
| 1444 | | $bytes *= GB_IN_BYTES; |
| 1445 | | } elseif ( false !== strpos( $value, 'm' ) ) { |
| 1446 | | $bytes *= MB_IN_BYTES; |
| 1447 | | } elseif ( false !== strpos( $value, 'k' ) ) { |
| 1448 | | $bytes *= KB_IN_BYTES; |
| | 1483 | // Count (of bytes) represented by value string. |
| | 1484 | $scalar = 0; |
| | 1485 | |
| | 1486 | // Sign of number represented by input, either positive (1) or negative (-1). |
| | 1487 | $sign = 1; |
| | 1488 | |
| | 1489 | // Numeric base of digits; determined by string prefix (e.g. "0x" or "0"). |
| | 1490 | $base = 10; |
| | 1491 | |
| | 1492 | // Index into input string as we walk through it and analyze each character. |
| | 1493 | $i = 0; |
| | 1494 | |
| | 1495 | // Trim leading whitespace. |
| | 1496 | for ( ; $i < $strlen; $i++ ) { |
| | 1497 | switch ( $value[ $i ] ) { |
| | 1498 | case ' ': |
| | 1499 | case '\t': |
| | 1500 | case '\r': |
| | 1501 | case '\v': |
| | 1502 | case '\f': |
| | 1503 | break; |
| | 1504 | |
| | 1505 | default: |
| | 1506 | break 2; |
| | 1507 | } |
| | 1508 | } |
| | 1509 | |
| | 1510 | // Handle optional sign indicator. |
| | 1511 | switch ( $value[ $i ] ) { |
| | 1512 | case '+': |
| | 1513 | $i++; |
| | 1514 | break; |
| | 1515 | |
| | 1516 | case '-': |
| | 1517 | $sign = -1; |
| | 1518 | $i++; |
| | 1519 | break; |
| | 1520 | } |
| | 1521 | |
| | 1522 | // Determine base for digit conversion, if not decimal. |
| | 1523 | $base_a = $value[ $i ]; |
| | 1524 | $base_b = $i + 1 < $strlen ? $value[ $i + 1 ] : ''; |
| | 1525 | |
| | 1526 | if ( '0' === $base_a && ( 'x' === $base_b || 'X' === $base_b ) ) { |
| | 1527 | $base = 16; |
| | 1528 | $i += 2; |
| | 1529 | } elseif ( '0' === $base_a && ctype_digit( $base_b ) ) { |
| | 1530 | $base = 8; |
| | 1531 | ++$i; |
| 1451 | | // Deal with large (float) values which run into the maximum integer size. |
| 1452 | | return min( $bytes, PHP_INT_MAX ); |
| | 1534 | // Trim leading zeros. |
| | 1535 | for ( ; $i < $strlen; $i++ ) { |
| | 1536 | if ( '0' !== $value[ $i ] ) { |
| | 1537 | break; |
| | 1538 | } |
| | 1539 | } |
| | 1540 | |
| | 1541 | // Numeric values for scanned digits. |
| | 1542 | $digits = array( |
| | 1543 | '0' => 0, |
| | 1544 | '1' => 1, |
| | 1545 | '2' => 2, |
| | 1546 | '3' => 3, |
| | 1547 | '4' => 4, |
| | 1548 | '5' => 5, |
| | 1549 | '6' => 6, |
| | 1550 | '7' => 7, |
| | 1551 | '8' => 8, |
| | 1552 | '9' => 9, |
| | 1553 | 'A' => 10, |
| | 1554 | 'a' => 10, |
| | 1555 | 'B' => 11, |
| | 1556 | 'b' => 11, |
| | 1557 | 'C' => 12, |
| | 1558 | 'c' => 12, |
| | 1559 | 'D' => 13, |
| | 1560 | 'd' => 13, |
| | 1561 | 'E' => 14, |
| | 1562 | 'e' => 14, |
| | 1563 | 'F' => 15, |
| | 1564 | 'f' => 15, |
| | 1565 | ); |
| | 1566 | |
| | 1567 | /* |
| | 1568 | * Build the scalar value by eating the |
| | 1569 | * next sequence of contiguous digits. |
| | 1570 | */ |
| | 1571 | for ( ; $i < $strlen; $i++ ) { |
| | 1572 | $c = $value[ $i ]; |
| | 1573 | |
| | 1574 | /* |
| | 1575 | * Only digits recognized in this base system can be used. |
| | 1576 | * Once we find an unrecognized digit we abort and move |
| | 1577 | * on to the next step in parsing the size suffix. |
| | 1578 | */ |
| | 1579 | if ( ! isset( $digits[ $c ] ) || $digits[ $c ] >= $base ) { |
| | 1580 | break; |
| | 1581 | } |
| | 1582 | |
| | 1583 | $scalar = $scalar * $base + $digits[ $c ]; |
| | 1584 | } |
| | 1585 | |
| | 1586 | /* |
| | 1587 | * Clamp the parsed digits to an integer value as PHP does internally. |
| | 1588 | * See `strtol()`/`LONG_MAX` for 32-bit systems and `strtoll()`/`LLONG_MAX` for 64-bit. |
| | 1589 | */ |
| | 1590 | if ( $sign > 0 && $scalar >= PHP_INT_MAX ) { |
| | 1591 | $scalar = PHP_INT_MAX; |
| | 1592 | } elseif ( $sign < 0 && $scalar >= -PHP_INT_MIN ) { |
| | 1593 | $scalar = PHP_INT_MIN; |
| | 1594 | } elseif ( $sign < 0 ) { |
| | 1595 | $scalar = -$scalar; |
| | 1596 | } |
| | 1597 | |
| | 1598 | /* |
| | 1599 | * Do not use WP constants here (GB_IN_BYTES, MB_IN_BYTES, KB_IN_BYTES) |
| | 1600 | * since they are re-definable; PHP shorthand values are hard-coded |
| | 1601 | * in PHP itself and stay the same regardless of these constants. |
| | 1602 | * |
| | 1603 | * Note that we can overflow here, as happens in PHP itself. |
| | 1604 | * Overflow results will likely not match PHP's value, but |
| | 1605 | * will likely break in most cases anyway and so leaving |
| | 1606 | * this loose is the best we can do until and unless PHP |
| | 1607 | * makes a more concrete choice on how to handle overflow. |
| | 1608 | * |
| | 1609 | * Fallthrough preserved to match structure and behavior in PHP. |
| | 1610 | */ |
| | 1611 | switch ( $value[ $strlen - 1 ] ) { |
| | 1612 | case 'g': |
| | 1613 | case 'G': |
| | 1614 | $scalar *= 1024; |
| | 1615 | // Fallthrough. |
| | 1616 | |
| | 1617 | case 'm': |
| | 1618 | case 'M': |
| | 1619 | $scalar *= 1024; |
| | 1620 | // Fallthrough. |
| | 1621 | |
| | 1622 | case 'k': |
| | 1623 | case 'K': |
| | 1624 | $scalar *= 1024; |
| | 1625 | // Fallthrough. |
| | 1626 | } |
| | 1627 | |
| | 1628 | return (int) $scalar; |