From 7daad4ceda35f0d1dd535673deb8090f3562b02d Mon Sep 17 00:00:00 2001 From: paranoiq Date: Tue, 26 Apr 2022 13:11:56 +0200 Subject: [PATCH] Added maxLength param for Str::join(); Fixed edge cases in Str::getLineAt() --- src/common/Str.php | 47 ++++++++++++++++++++++------ tests/src/common/Str.phpt | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/common/Str.php b/src/common/Str.php index c5e815d9..65842ef7 100644 --- a/src/common/Str.php +++ b/src/common/Str.php @@ -24,6 +24,7 @@ use const MB_CASE_TITLE; use function array_keys; use function array_pop; +use function array_shift; use function array_values; use function class_exists; use function count; @@ -250,38 +251,64 @@ public static function splitByLast(string $string, string $search): array return [substr($string, 0, $pos), substr($string, $pos + 1)]; } - public static function getLineAt(string $string, int $position, string $separator = "\n"): string + /** + * Position is per bytes, not per characters! + */ + public static function getLineAt(string $string, int $bytePosition, string $separator = "\n"): string { - $before = substr($string, 0, $position); + if ($bytePosition >= strlen($string)) { + return ''; + } + + $before = self::substring($string, 0, $bytePosition); $lineStart = strrpos($before, $separator); if ($lineStart === false) { - $lineStart = 0; + $lineStart = -1; } - $lineEnd = strpos($string, $separator, $position); + $lineEnd = strpos($string, $separator, $bytePosition); if ($lineEnd === false) { - $lineEnd = strlen($string) - 1; + $lineEnd = strlen($string); } return substr($string, $lineStart + 1, $lineEnd - $lineStart - 1); } /** - * Implode with optional different separator for last item in the list ("A, B, C and D") + * Implode with optional different separator for last item in the list ("A, B, C and D") and length limit ("A, B, C...") * @param string[] $items */ - public static function join(array $items, string $separator = '', ?string $lastSeparator = null): string + public static function join( + array $items, + string $separator = '', + ?string $lastSeparator = null, + ?int $maxLength = null, + string $ellipsis = '…' + ): string { if (count($items) === 0) { return ''; } elseif (count($items) === 1) { return (string) array_pop($items); - } elseif ($lastSeparator === null) { - return implode($separator, $items); + } + + if ($lastSeparator === null) { + $result = implode($separator, $items); } else { $last = array_pop($items); - return implode($separator, $items) . $lastSeparator . $last; + $result = implode($separator, $items) . $lastSeparator . $last; + } + if ($maxLength === null || self::length($result) <= $maxLength) { + return $result; + } + + $maxLength -= self::length($ellipsis); + $result = (string) array_shift($items); + while (self::length($result) < $maxLength && $items !== []) { + $result .= $separator . array_shift($items); } + + return $result . $ellipsis; } /** diff --git a/tests/src/common/Str.phpt b/tests/src/common/Str.phpt index 5dba6787..f37f38c6 100644 --- a/tests/src/common/Str.phpt +++ b/tests/src/common/Str.phpt @@ -8,6 +8,12 @@ use Dogma\Tester\Assert; require_once __DIR__ . '/../bootstrap.php'; +between: +Assert::same(Str::between('abc@def#ghi', '@', '#'), 'def'); +Assert::same(Str::between('abc@def#ghi', '#', '@'), null); +Assert::same(Str::between('abc@def#ghi@jkl', '#', '@'), 'ghi'); + + toFirst: Assert::same(Str::toFirst('abc@def', '@'), 'abc'); Assert::same(Str::toFirst('abc@def', '#'), 'abc@def'); @@ -28,10 +34,66 @@ Assert::same(Str::splitByLast('abc@def', '@'), ['abc', 'def']); Assert::same(Str::splitByLast('abc@def@ghi', '@'), ['abc@def', 'ghi']); +getLineAt: +Assert::same(Str::getLineAt("111\n222\n333", 0), "111"); +Assert::same(Str::getLineAt("111\n222\n333", 1), "111"); +Assert::same(Str::getLineAt("111\n222\n333", 2), "111"); +Assert::same(Str::getLineAt("111\n222\n333", 3), "111"); // \n belongs to previous +Assert::same(Str::getLineAt("111\n222\n333", 4), "222"); +Assert::same(Str::getLineAt("111\n222\n333", 5), "222"); +Assert::same(Str::getLineAt("111\n222\n333", 6), "222"); +Assert::same(Str::getLineAt("111\n222\n333", 7), "222"); // -//- +Assert::same(Str::getLineAt("111\n222\n333", 8), "333"); +Assert::same(Str::getLineAt("111\n222\n333", 9), "333"); +Assert::same(Str::getLineAt("111\n222\n333", 10), "333"); +Assert::same(Str::getLineAt("111\n222\n333", 11), ""); // after end +Assert::same(Str::getLineAt("111\n222\n333", 12), ""); + + +join: +Assert::same(Str::join([], ', ', ' and '), ''); +Assert::same(Str::join([1], ', ', ' and '), '1'); +Assert::same(Str::join([1, 2], ', ', ' and '), '1 and 2'); +Assert::same(Str::join([1, 2, 3], ', ', ' and '), '1, 2 and 3'); +Assert::same(Str::join([1, 2, 3, 4], ', ', ' and '), '1, 2, 3 and 4'); + +// with limit +Assert::same(Str::join([], ', ', ' and ', 10), ''); +Assert::same(Str::join([1], ', ', ' and ', 10), '1'); +Assert::same(Str::join([1, 2], ', ', ' and ', 10), '1 and 2'); +Assert::same(Str::join([1, 2, 3], ', ', ' and ', 10), '1, 2 and 3'); +Assert::same(Str::join([1, 2, 3, 4], ', ', ' and ', 10), '1, 2, 3…'); // and won't fit +Assert::same(Str::join([1, 2, 3, 4, 5], ', ', ' and ', 10), '1, 2, 3, 4…'); +Assert::same(Str::join([1, 2, 3, 4], ', ', ' and ', 10, '...'), '1, 2, 3...'); +Assert::same(Str::join([1, 2, 3, 4, 5], ', ', ' and ', 10, '...'), '1, 2, 3...'); + + +count: +Assert::same(Str::count('1, 2, 3, 4', ','), 3); +Assert::same(Str::count('1, 2, 3, 4', 'and'), 0); +Assert::same(Str::count('1, 2, 3 and 4', ','), 2); +Assert::same(Str::count('1, 2, 3 and 4', 'and'), 1); + + +replaceKeys: +Assert::same(Str::replaceKeys('1, 2, 3 and 4', [',' => ';', 'and' => 'or']), '1; 2; 3 or 4'); + + +underscore: +Assert::same(Str::underscore('fooBarBaz'), 'foo_bar_baz'); +Assert::same(Str::underscore('FooBarBaz'), 'foo_bar_baz'); + + trimLinesRight: Assert::same(Str::trimLinesRight("foo \n bar\t\n\tbaz"), "foo\n bar\n\tbaz"); +equals: + + +compare: + + findTag: Assert::same(Str::findTag(' foo ', '{', '}'), [null, null]); Assert::same(Str::findTag(' {foo} ', '{', '}'), [1, 5]); @@ -83,3 +145,6 @@ Assert::same(Str::optimalDistanceBin('ódy', 'óyd'), 1); removeDiacritics: Assert::same(Str::removeDiacritics('příliš žluťoučký kůň úpěl ďábelské ódy'), 'prilis zlutoucky kun upel dabelske ody'); Assert::same(Str::removeDiacritics('PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY'), 'PRILIS ZLUTOUCKY KUN UPEL DABELSKE ODY'); + + +convertEncoding: