diff --git a/composer.json b/composer.json index fedaad94..de914c9c 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "ext-bcmath": "*", "ext-json": "*", "ext-mbstring": "*", - "ext-sodium": "*" + "ext-sodium": "*", + "ext-intl": "*" }, "require-dev": { "phpunit/phpunit": "^9.4", diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index aa03791b..9d9ba8b9 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -336,6 +336,20 @@ final class Loader 'Psl\Hash\equals', 'Psl\Hash\Hmac\hash', 'Psl\Hash\Hmac\algorithms', + 'Psl\Str\Grapheme\contains', + 'Psl\Str\Grapheme\contains_ci', + 'Psl\Str\Grapheme\ends_with', + 'Psl\Str\Grapheme\ends_with_ci', + 'Psl\Str\Grapheme\length', + 'Psl\Str\Grapheme\search', + 'Psl\Str\Grapheme\search_ci', + 'Psl\Str\Grapheme\search_last', + 'Psl\Str\Grapheme\search_last_ci', + 'Psl\Str\Grapheme\slice', + 'Psl\Str\Grapheme\starts_with', + 'Psl\Str\Grapheme\starts_with_ci', + 'Psl\Str\Grapheme\strip_prefix', + 'Psl\Str\Grapheme\strip_suffix', ]; public const INTERFACES = [ diff --git a/src/Psl/Str/Grapheme/contains.php b/src/Psl/Str/Grapheme/contains.php new file mode 100644 index 00000000..daf207d1 --- /dev/null +++ b/src/Psl/Str/Grapheme/contains.php @@ -0,0 +1,30 @@ + $total_length) { + return false; + } + + /** @psalm-suppress MissingThrowsDocblock */ + $position = search_last($string, $suffix); + if (null === $position) { + return false; + } + + return $position + $suffix_length === $total_length; +} diff --git a/src/Psl/Str/Grapheme/ends_with_ci.php b/src/Psl/Str/Grapheme/ends_with_ci.php new file mode 100644 index 00000000..9d346623 --- /dev/null +++ b/src/Psl/Str/Grapheme/ends_with_ci.php @@ -0,0 +1,36 @@ + $total_length) { + return false; + } + + /** @psalm-suppress MissingThrowsDocblock */ + $position = search_last_ci($string, $suffix); + if (null === $position) { + return false; + } + + return $position + $suffix_length === $total_length; +} diff --git a/src/Psl/Str/Grapheme/length.php b/src/Psl/Str/Grapheme/length.php new file mode 100644 index 00000000..c0ead6ec --- /dev/null +++ b/src/Psl/Str/Grapheme/length.php @@ -0,0 +1,27 @@ += -$haystack_length && $offset <= $haystack_length, 'Offset is out-of-bounds.'); + + return false === ($pos = grapheme_strrpos($haystack, $needle, $offset)) ? + null : + $pos; +} diff --git a/src/Psl/Str/Grapheme/search_last_ci.php b/src/Psl/Str/Grapheme/search_last_ci.php new file mode 100644 index 00000000..d1295e47 --- /dev/null +++ b/src/Psl/Str/Grapheme/search_last_ci.php @@ -0,0 +1,35 @@ += -$haystack_length && $offset <= $haystack_length, 'Offset is out-of-bounds.'); + + return false === ($pos = grapheme_strripos($haystack, $needle, $offset)) ? + null : + $pos; +} diff --git a/src/Psl/Str/Grapheme/slice.php b/src/Psl/Str/Grapheme/slice.php new file mode 100644 index 00000000..0b2a5f0e --- /dev/null +++ b/src/Psl/Str/Grapheme/slice.php @@ -0,0 +1,36 @@ += 0, 'Expected a non-negative length.'); + $string_length = length($string); + $offset = Psl\Internal\validate_offset($offset, $string_length); + + if (0 === $offset && (null === $length || $string_length <= $length)) { + return $string; + } + + if (null === $length) { + return (string) grapheme_substr($string, $offset); + } + + return (string) grapheme_substr($string, $offset, $length); +} diff --git a/src/Psl/Str/Grapheme/starts_with.php b/src/Psl/Str/Grapheme/starts_with.php new file mode 100644 index 00000000..ba845324 --- /dev/null +++ b/src/Psl/Str/Grapheme/starts_with.php @@ -0,0 +1,16 @@ +expectException(Exception\InvariantViolationException::class); + + Grapheme\slice('Hello', 0, -1); + } + + public function testSliceThrowsForOutOfBoundOffset(): void + { + $this->expectException(Exception\InvariantViolationException::class); + + Grapheme\slice('Hello', 10); + } + + public function testSliceThrowsForNegativeOutOfBoundOffset(): void + { + $this->expectException(Exception\InvariantViolationException::class); + + Grapheme\slice('hello', -6); + } +} diff --git a/tests/Psl/Str/Grapheme/StartsWithCiTest.php b/tests/Psl/Str/Grapheme/StartsWithCiTest.php new file mode 100644 index 00000000..51d3588d --- /dev/null +++ b/tests/Psl/Str/Grapheme/StartsWithCiTest.php @@ -0,0 +1,41 @@ +