diff --git a/src/Utils/Arrays.php b/src/Utils/Arrays.php index 81d030936..625bec65e 100644 --- a/src/Utils/Arrays.php +++ b/src/Utils/Arrays.php @@ -190,8 +190,9 @@ public static function renameKey(array &$array, string|int $oldKey, string|int $ * @param string[] $array * @return string[] */ - public static function grep(array $array, string $pattern, int $flags = 0): array + public static function grep(array $array, string $pattern, bool|int $invert = false): array { + $flags = $invert ? PREG_GREP_INVERT : 0; return Strings::pcre('preg_grep', [$pattern, $array, $flags]); } diff --git a/src/Utils/Image.php b/src/Utils/Image.php index 8d4238e8c..f8455322f 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -98,7 +98,7 @@ class Image { use Nette\SmartObject; - /** {@link resize()} only shrinks images */ + /** @deprecated */ public const SHRINK_ONLY = 0b0001; /** {@link resize()} will ignore aspect ratio */ @@ -303,14 +303,19 @@ public function getImageResource(): \GdImage /** * Scales an image. Width and height accept pixels or percent. + * @param self::FIT|self::FILL|self::STRETCH|self::EXACT $mode */ - public function resize(int|string|null $width, int|string|null $height, int $flags = self::FIT): static - { - if ($flags & self::EXACT) { + public function resize( + int|string|null $width, + int|string|null $height, + int $mode = self::FIT, + bool $shrinkOnly = false, + ): static { + if ($mode & self::EXACT) { return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height); } - [$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags); + [$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode, $shrinkOnly); if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize $newImage = static::fromBlank($newWidth, $newHeight, self::rgb(0, 0, 0, 127))->getImageResource(); @@ -338,13 +343,15 @@ public function resize(int|string|null $width, int|string|null $height, int $fla /** * Calculates dimensions of resized image. Width and height accept pixels or percent. + * @param self::FIT|self::FILL|self::STRETCH $mode */ public static function calculateSize( int $srcWidth, int $srcHeight, $newWidth, $newHeight, - int $flags = self::FIT, + int $mode = self::FIT, + bool $shrinkOnly = false, ): array { if ($newWidth === null) { } elseif (self::isPercent($newWidth)) { @@ -357,17 +364,17 @@ public static function calculateSize( if ($newHeight === null) { } elseif (self::isPercent($newHeight)) { $newHeight = (int) round($srcHeight / 100 * abs($newHeight)); - $flags |= empty($percents) ? 0 : self::STRETCH; + $mode |= empty($percents) ? 0 : self::STRETCH; } else { $newHeight = abs($newHeight); } - if ($flags & self::STRETCH) { // non-proportional + if ($mode & self::STRETCH) { // non-proportional if (!$newWidth || !$newHeight) { throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.'); } - if ($flags & self::SHRINK_ONLY) { + if ($mode & self::SHRINK_ONLY || $shrinkOnly) { $newWidth = (int) round($srcWidth * min(1, $newWidth / $srcWidth)); $newHeight = (int) round($srcHeight * min(1, $newHeight / $srcHeight)); } @@ -386,11 +393,11 @@ public static function calculateSize( $scale[] = $newHeight / $srcHeight; } - if ($flags & self::FILL) { + if ($mode & self::FILL) { $scale = [max($scale)]; } - if ($flags & self::SHRINK_ONLY) { + if ($mode & self::SHRINK_ONLY || $shrinkOnly) { $scale[] = 1; } diff --git a/src/Utils/Json.php b/src/Utils/Json.php index 9f06a7f05..8204b3175 100644 --- a/src/Utils/Json.php +++ b/src/Utils/Json.php @@ -19,23 +19,33 @@ final class Json { use Nette\StaticClass; + /** @deprecated */ public const FORCE_ARRAY = 0b0001; + /** @deprecated */ public const PRETTY = 0b0010; + /** @deprecated */ public const ESCAPE_UNICODE = 0b0100; /** - * Converts value to JSON format. The flag can be Json::PRETTY, which formats JSON for easier reading and clarity, - * and Json::ESCAPE_UNICODE for ASCII output. + * Converts value to JSON format. Parameter $flags is deprecated. Use $pretty for easier reading and clarity, + * and $escapeUnicode for ASCII output. * @throws JsonException */ - public static function encode(mixed $value, int $flags = 0): string - { - $flags = ($flags & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) + public static function encode( + mixed $value, + bool|int $pretty = false, + bool $escapeUnicode = false, + ): string { + if (is_int($pretty)) { // back compatibility + $escapeUnicode = $pretty & self::ESCAPE_UNICODE; + $pretty &= self::PRETTY; + } + $flags = ($escapeUnicode ? 0 : JSON_UNESCAPED_UNICODE) | JSON_UNESCAPED_SLASHES - | ($flags & self::PRETTY ? JSON_PRETTY_PRINT : 0) + | ($pretty ? JSON_PRETTY_PRINT : 0) | (defined('JSON_PRESERVE_ZERO_FRACTION') ? JSON_PRESERVE_ZERO_FRACTION : 0); // since PHP 5.6.6 & PECL JSON-C 1.3.7 $json = json_encode($value, $flags); @@ -47,13 +57,12 @@ public static function encode(mixed $value, int $flags = 0): string /** - * Parses JSON to PHP value. The flag can be Json::FORCE_ARRAY, which forces an array instead of an object as the return value. + * Parses JSON to PHP value. Parameter $forceArray forces an array instead of an object as the return value. * @throws JsonException */ - public static function decode(string $json, int $flags = 0): mixed + public static function decode(string $json, bool|int $forceArray = false): mixed { - $forceArray = (bool) ($flags & self::FORCE_ARRAY); - $value = json_decode($json, $forceArray, 512, JSON_BIGINT_AS_STRING); + $value = json_decode($json, (bool) $forceArray, 512, JSON_BIGINT_AS_STRING); if ($error = json_last_error()) { throw new JsonException(json_last_error_msg(), $error); } diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index e3832ea48..44020c4a0 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -468,20 +468,33 @@ private static function pos(string $haystack, string $needle, int $nth = 1): ?in /** * Splits a string into array by the regular expression. Parenthesized expression in the delimiter are captured. - * Parameter $flags can be any combination of PREG_SPLIT_NO_EMPTY and PREG_OFFSET_CAPTURE flags. */ - public static function split(string $subject, string $pattern, int $flags = 0): array - { + public static function split( + string $subject, + string $pattern, + bool|int $captureOffset = false, + bool $skipEmpty = false, + ): array { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]); } /** * Checks if given string matches a regular expression pattern and returns an array with first found match and each subpattern. - * Parameter $flags can be any combination of PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL flags. */ - public static function match(string $subject, string $pattern, int $flags = 0, int $offset = 0): ?array - { + public static function match( + string $subject, + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + ): ?array { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); if ($offset > strlen($subject)) { return null; } @@ -492,11 +505,20 @@ public static function match(string $subject, string $pattern, int $flags = 0, i /** - * Finds all occurrences matching regular expression pattern and returns a two-dimensional array. Result is array of matches (ie uses by default PREG_SET_ORDER). - * Parameter $flags can be any combination of PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL and PREG_PATTERN_ORDER flags. + * Finds all occurrences matching regular expression pattern and returns a two-dimensional array. + * Result is array of matches (ie uses by default PREG_SET_ORDER). */ - public static function matchAll(string $subject, string $pattern, int $flags = 0, int $offset = 0): array - { + public static function matchAll( + string $subject, + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $patternOrder = false, + ): array { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); if ($offset > strlen($subject)) { return []; } diff --git a/tests/Utils/Arrays.grep().phpt b/tests/Utils/Arrays.grep().phpt index f356405d3..f8613b8ae 100644 --- a/tests/Utils/Arrays.grep().phpt +++ b/tests/Utils/Arrays.grep().phpt @@ -21,3 +21,8 @@ Assert::same([ 0 => 'a', 2 => 'c', ], Arrays::grep(['a', '1', 'c'], '#\d#', PREG_GREP_INVERT)); + +Assert::same([ + 0 => 'a', + 2 => 'c', +], Arrays::grep(['a', '1', 'c'], '#\d#', invert: true)); diff --git a/tests/Utils/Image.resize.phpt b/tests/Utils/Image.resize.phpt index ef8a79887..7b3663cce 100644 --- a/tests/Utils/Image.resize.phpt +++ b/tests/Utils/Image.resize.phpt @@ -52,6 +52,14 @@ test('resizing Y shrink', function () use ($main) { }); +test('resizing Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(null, 150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('resizing X Y shrink', function () use ($main) { $image = clone $main; $image->resize(300, 150, Image::SHRINK_ONLY); @@ -60,6 +68,14 @@ test('resizing X Y shrink', function () use ($main) { }); +test('resizing X Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(300, 150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('resizing X Y', function () use ($main) { $image = clone $main; $image->resize(300, 150); @@ -84,6 +100,14 @@ test('resizing X Y shrink stretch', function () use ($main) { }); +test('resizing X Y shrink stretch', function () use ($main) { + $image = clone $main; + $image->resize(300, 100, Image::STRETCH, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(100, $image->height); +}); + + test('resizing X%', function () use ($main) { $image = clone $main; $image->resize('110%', null); @@ -116,6 +140,14 @@ test('flipping Y shrink', function () use ($main) { }); +test('flipping Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(null, -150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('flipping X Y shrink', function () use ($main) { $image = clone $main; $image->resize(-300, -150, Image::SHRINK_ONLY); @@ -124,6 +156,14 @@ test('flipping X Y shrink', function () use ($main) { }); +test('flipping X Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(-300, -150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('exact resize', function () use ($main) { $image = clone $main; $image->resize(300, 150, Image::EXACT); diff --git a/tests/Utils/Json.decode().phpt b/tests/Utils/Json.decode().phpt index e9910d26c..b8ffabe43 100644 --- a/tests/Utils/Json.decode().phpt +++ b/tests/Utils/Json.decode().phpt @@ -20,6 +20,7 @@ Assert::null(Json::decode(' null')); Assert::equal((object) ['a' => 1], Json::decode('{"a":1}')); Assert::same(['a' => 1], Json::decode('{"a":1}', Json::FORCE_ARRAY)); +Assert::same(['a' => 1], Json::decode('{"a":1}', forceArray: true)); Assert::exception(function () { diff --git a/tests/Utils/Json.encode().phpt b/tests/Utils/Json.encode().phpt index 9ba173388..5f072c366 100644 --- a/tests/Utils/Json.encode().phpt +++ b/tests/Utils/Json.encode().phpt @@ -36,10 +36,12 @@ Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}")); // ESCAPE_UNICODE Assert::same('"/I\u00f1t\u00ebrn\u00e2ti\u00f4n\u00e0liz\u00e6ti\u00f8n"', Json::encode("/I\u{F1}t\u{EB}rn\u{E2}ti\u{F4}n\u{E0}liz\u{E6}ti\u{F8}n", Json::ESCAPE_UNICODE)); Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}", Json::ESCAPE_UNICODE)); +Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}", escapeUnicode: true)); // JSON_PRETTY_PRINT Assert::same("[\n 1,\n 2,\n 3\n]", Json::encode([1, 2, 3], Json::PRETTY)); +Assert::same("[\n 1,\n 2,\n 3\n]", Json::encode([1, 2, 3], pretty: true)); Assert::exception(function () { diff --git a/tests/Utils/Strings.match().phpt b/tests/Utils/Strings.match().phpt index 5814b5753..314472c01 100644 --- a/tests/Utils/Strings.match().phpt +++ b/tests/Utils/Strings.match().phpt @@ -20,6 +20,7 @@ Assert::same(['hell', 'l'], Strings::match('hello world!', '#([e-l])+#')); Assert::same(['hell'], Strings::match('hello world!', '#[e-l]+#')); Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', PREG_OFFSET_CAPTURE)); +Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', captureOffset: true)); Assert::same(['ll'], Strings::match('hello world!', '#[e-l]+#', 0, 2)); diff --git a/tests/Utils/Strings.matchAll().phpt b/tests/Utils/Strings.matchAll().phpt index 384bbc18a..bc81c4bee 100644 --- a/tests/Utils/Strings.matchAll().phpt +++ b/tests/Utils/Strings.matchAll().phpt @@ -32,14 +32,28 @@ Assert::same([ [['k', 14], ['k', 14], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', PREG_OFFSET_CAPTURE)); +Assert::same([ + [['lu', 2], ['l', 2], ['u', 3]], + [['ou', 6], ['o', 6], ['u', 7]], + [['k', 10], ['k', 10], ['', 11]], + [['k', 14], ['k', 14], ['', 15]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true)); + Assert::same([ [['lu', 2], ['ou', 6], ['k', 10], ['k', 14]], [['l', 2], ['o', 6], ['k', 10], ['k', 14]], [['u', 3], ['u', 7], ['', 11], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER)); +Assert::same([ + [['lu', 2], ['ou', 6], ['k', 10], ['k', 14]], + [['l', 2], ['o', 6], ['k', 10], ['k', 14]], + [['u', 3], ['u', 7], ['', 11], ['', 15]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true, patternOrder: true)); + Assert::same([['l'], ['k'], ['k']], Strings::matchAll('žluťoučký kůň', '#[e-l]+#u', 0, 2)); Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', PREG_PATTERN_ORDER, 2)); +Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', patternOrder: true, offset: 2)); Assert::same([], Strings::matchAll('hello world!', '', 0, 50)); diff --git a/tests/Utils/Strings.split().phpt b/tests/Utils/Strings.split().phpt index b291e9613..3a57a4c4e 100644 --- a/tests/Utils/Strings.split().phpt +++ b/tests/Utils/Strings.split().phpt @@ -29,6 +29,14 @@ Assert::same([ 'c', ], Strings::split('a, b, c', '#(,)\s*#', PREG_SPLIT_NO_EMPTY)); +Assert::same([ + 'a', + ',', + 'b', + ',', + 'c', +], Strings::split('a, b, c', '#(,)\s*#', skipEmpty: true)); + Assert::same([ ['a', 0], [',', 1], @@ -36,3 +44,11 @@ Assert::same([ [',', 4], ['c', 6], ], Strings::split('a, b, c', '#(,)\s*#', PREG_SPLIT_OFFSET_CAPTURE)); + +Assert::same([ + ['a', 0], + [',', 1], + ['b', 3], + [',', 4], + ['c', 6], +], Strings::split('a, b, c', '#(,)\s*#', captureOffset: true));