From 1eadc4d0311acec5a9e97a1e72f63d9b041ba535 Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 2 Oct 2026 17:48:44 +0200 Subject: [PATCH] Accept php.ini style memory limits and reject too large values memoryLimit now also accepts K, M and G, so values like 512M from php.ini work. Values that overflow an integer threw a TypeError or were silently capped at PHP_INT_MAX, they now throw InvalidFormat. --- docs/getting-started.md | 12 ++++++------ src/Bytes.php | 14 ++++++++++++-- src/InvalidFormat.php | 2 +- tests/Unit/BytesTest.php | 13 +++++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 5153d0c..3089c1e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -34,16 +34,16 @@ stopping always happens *between* iterations, so your job is never interrupted h All options are optional. Without limits the worker runs until it is stopped via `$stop()`, `$worker->stop()` or a SIGTERM signal. -| Option | Type | Description | -|---------------|----------|---------------------------------------------------------------------------------------------------------------------------------| -| `runLimit` | `int` | Stop after this number of iterations. | -| `memoryLimit` | `string` | Stop when memory usage exceeds this value, e.g. `128MB`. Supported units: `B`, `KB`, `MB`, `GB` (case-insensitive, 1024-based). | -| `timeLimit` | `int` | Stop after this number of seconds. | +| Option | Type | Description | +|---------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| `runLimit` | `int` | Stop after this number of iterations. | +| `memoryLimit` | `string` | Stop when memory usage exceeds this value, e.g. `128MB` or `128M`. Supported units: `B`, `K`/`KB`, `M`/`MB`, `G`/`GB` (case-insensitive, 1024-based). | +| `timeLimit` | `int` | Stop after this number of seconds. | Limits are checked after each iteration. When a limit is exceeded, the worker logs the reason and stops gracefully. :::warning -An invalid `memoryLimit` string throws a `Patchlevel\Worker\InvalidFormat` exception. +An invalid or too large `memoryLimit` string throws a `Patchlevel\Worker\InvalidFormat` exception. ::: :::note diff --git a/src/Bytes.php b/src/Bytes.php index 91fefea..0d57b2f 100644 --- a/src/Bytes.php +++ b/src/Bytes.php @@ -5,6 +5,7 @@ namespace Patchlevel\Worker; use function array_key_exists; +use function is_int; use function preg_match; use function sprintf; use function strtoupper; @@ -13,8 +14,11 @@ final class Bytes { private const SIZES = [ 'B' => 1, + 'K' => 1_024, 'KB' => 1_024, + 'M' => 1_048_576, 'MB' => 1_048_576, + 'G' => 1_073_741_824, 'GB' => 1_073_741_824, ]; @@ -36,14 +40,20 @@ public static function parseFromString(string $string): self throw new InvalidFormat($string); } - $number = (int)$matches[1]; $unit = strtoupper($matches[2] ?? 'B'); if (!array_key_exists($unit, self::SIZES)) { throw new InvalidFormat($string); } - return new self($number * self::SIZES[$unit]); + $bytes = $matches[1] * self::SIZES[$unit]; + + // integer overflow results in a float + if (!is_int($bytes)) { + throw new InvalidFormat($string); + } + + return new self($bytes); } public function formatted(): string diff --git a/src/InvalidFormat.php b/src/InvalidFormat.php index c962846..3804ba5 100644 --- a/src/InvalidFormat.php +++ b/src/InvalidFormat.php @@ -12,6 +12,6 @@ final class InvalidFormat extends InvalidArgumentException { public function __construct(string $message) { - parent::__construct(sprintf('Invalid byte format received (got: "%s"). The format must consist of a number and a unit. The following units are allowed: B, KB, MB, GB', $message)); + parent::__construct(sprintf('Invalid byte format received (got: "%s"). The format must consist of a number and a unit. The following units are allowed: B, K, KB, M, MB, G, GB', $message)); } } diff --git a/tests/Unit/BytesTest.php b/tests/Unit/BytesTest.php index ab358fb..088c3b5 100644 --- a/tests/Unit/BytesTest.php +++ b/tests/Unit/BytesTest.php @@ -28,6 +28,10 @@ public static function invalidFormatProvider(): Generator yield ['-5GB']; yield ['505Foo']; yield ['50Kb50']; + yield ['50T']; + yield ['50TB']; + yield ['99999999999GB']; + yield ['99999999999999999999']; } #[DataProvider('validParseDataProvider')] @@ -55,6 +59,15 @@ public static function validParseDataProvider(): Generator yield ['50Gb', 53_687_091_200, '50.0 GiB']; yield ['50gb', 53_687_091_200, '50.0 GiB']; + yield ['50K', 51_200, '50.0 KiB']; + yield ['50k', 51_200, '50.0 KiB']; + yield ['512M', 536_870_912, '512.0 MiB']; + yield ['512m', 536_870_912, '512.0 MiB']; + yield ['1G', 1_073_741_824, '1.0 GiB']; + yield ['1g', 1_073_741_824, '1.0 GiB']; + + yield ['0050MB', 52_428_800, '50.0 MiB']; + yield ['1024b', 1024, '1.0 KiB']; yield ['1024Kb', 1_048_576, '1.0 MiB']; yield ['1024Mb', 1_073_741_824, '1.0 GiB'];