mirror of
https://github.com/danog/endtoend-test-psl.git
synced 2024-11-30 04:39:48 +01:00
[Str] Add the ability to supply string encoding (#55)
This commit is contained in:
parent
c471b63706
commit
25480b41fa
@ -101,6 +101,7 @@ final class Loader
|
||||
'Psl\Internal\validate_offset',
|
||||
'Psl\Internal\validate_offset_lower_bound',
|
||||
'Psl\Internal\lazy_iterator',
|
||||
'Psl\Internal\internal_encoding',
|
||||
'Psl\Iter\all',
|
||||
'Psl\Iter\any',
|
||||
'Psl\Iter\apply',
|
||||
|
26
src/Psl/Internal/internal_encoding.php
Normal file
26
src/Psl/Internal/internal_encoding.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Internal;
|
||||
|
||||
use Psl;
|
||||
use Psl\Type;
|
||||
use Psl\Exception;
|
||||
|
||||
use function in_array;
|
||||
use function mb_internal_encoding;
|
||||
use function mb_list_encodings;
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-suppress ImpureFunctionCall
|
||||
*
|
||||
* @throws Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function internal_encoding(?string $encoding = null): string
|
||||
{
|
||||
Psl\invariant(null === $encoding || in_array($encoding, mb_list_encodings(), true), 'Invalid encoding.');
|
||||
return $encoding ?? (Type\is_string($internal_encoding = mb_internal_encoding()) ? $internal_encoding : 'UTF-8');
|
||||
}
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl\Exception;
|
||||
|
||||
/**
|
||||
* Returns the string with the first character capitalized.
|
||||
*
|
||||
@ -25,13 +27,17 @@ namespace Psl\Str;
|
||||
* => Str('1337)
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function capitalize(string $string): string
|
||||
function capitalize(string $string, ?string $encoding = null): string
|
||||
{
|
||||
if ('' === $string) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock - $offset is within-bounds */
|
||||
return concat(uppercase(slice($string, 0, 1)), slice($string, 1, length($string)));
|
||||
return concat(
|
||||
uppercase(slice($string, 0, 1, $encoding), $encoding),
|
||||
slice($string, 1, length($string, $encoding), $encoding)
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl\Exception;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_convert_case;
|
||||
|
||||
use const MB_CASE_TITLE;
|
||||
|
||||
/**
|
||||
* Returns the string with all words capitalized.
|
||||
*
|
||||
@ -22,8 +29,10 @@ namespace Psl\Str;
|
||||
* => Str('مرحبا بكم')
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function capitalize_words(string $string): string
|
||||
function capitalize_words(string $string, ?string $encoding = null): string
|
||||
{
|
||||
return \mb_convert_case($string, \MB_CASE_TITLE, encoding($string));
|
||||
return mb_convert_case($string, MB_CASE_TITLE, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_chr;
|
||||
|
||||
/**
|
||||
* Return a specific character.
|
||||
@ -18,11 +21,13 @@ use Psl;
|
||||
* => Str('ل')
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function chr(int $ascii): string
|
||||
function chr(int $ascii, ?string $encoding = null): string
|
||||
{
|
||||
/** @var string|false $char */
|
||||
$char = \mb_chr($ascii, 'UTF-8');
|
||||
$char = mb_chr($ascii, Internal\internal_encoding($encoding));
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock */
|
||||
Psl\invariant(is_string($char), 'Unexpected Error.');
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
/**
|
||||
* Returns an array containing the string split into chunks of the given size.
|
||||
@ -34,8 +35,9 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the given $chunk_size is negative or above the limit ( 65535 ).
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function chunk(string $string, int $chunk_size = 1): array
|
||||
function chunk(string $string, int $chunk_size = 1, ?string $encoding = null): array
|
||||
{
|
||||
Psl\invariant($chunk_size >= 1, 'Expected a non-negative chunk size.');
|
||||
if ('' === $string) {
|
||||
@ -45,5 +47,5 @@ function chunk(string $string, int $chunk_size = 1): array
|
||||
Psl\invariant(65535 >= $chunk_size, 'Maximum chunk length must not exceed 65535.');
|
||||
|
||||
/** @psalm-var list<string> */
|
||||
return mb_str_split($string, $chunk_size, encoding($string));
|
||||
return mb_str_split($string, $chunk_size, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -37,14 +37,15 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function contains(string $haystack, string $needle, int $offset = 0): bool
|
||||
function contains(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): bool
|
||||
{
|
||||
if ('' === $needle) {
|
||||
Psl\Internal\validate_offset($offset, length($haystack));
|
||||
Psl\Internal\validate_offset($offset, length($haystack, $encoding));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== search($haystack, $needle, $offset);
|
||||
return null !== search($haystack, $needle, $offset, $encoding);
|
||||
}
|
||||
|
@ -37,14 +37,15 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function contains_ci(string $haystack, string $needle, int $offset = 0): bool
|
||||
function contains_ci(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): bool
|
||||
{
|
||||
if ('' === $needle) {
|
||||
Psl\Internal\validate_offset($offset, length($haystack));
|
||||
Psl\Internal\validate_offset($offset, length($haystack, $encoding));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== search_ci($haystack, $needle, $offset);
|
||||
return null !== search_ci($haystack, $needle, $offset, $encoding);
|
||||
}
|
||||
|
@ -4,10 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use function mb_detect_encoding;
|
||||
|
||||
/**
|
||||
* Detect the encoding of the giving string.
|
||||
*
|
||||
* @psalm-return null|string The string encoding or null if unable to detect encoding.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
function encoding(string $str): string
|
||||
function encoding(string $string): ?string
|
||||
{
|
||||
return \mb_detect_encoding($str, null, true) ?: 'UTF-8';
|
||||
return mb_detect_encoding($string, null, true) ?: null;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns whether the string ends with the given suffix.
|
||||
*
|
||||
@ -31,21 +33,22 @@ namespace Psl\Str;
|
||||
* => Bool(false)
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function ends_with(string $string, string $suffix): bool
|
||||
function ends_with(string $string, string $suffix, ?string $encoding = null): bool
|
||||
{
|
||||
if ($suffix === $string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$suffix_length = length($suffix);
|
||||
$total_length = length($string);
|
||||
$suffix_length = length($suffix, $encoding);
|
||||
$total_length = length($string, $encoding);
|
||||
if ($suffix_length > $total_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock - we don't supply $offset */
|
||||
$position = search_last($string, $suffix);
|
||||
$position = search_last($string, $suffix, 0, $encoding);
|
||||
if (null === $position) {
|
||||
return false;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns whether the string ends with the given suffix (case-insensitive).
|
||||
*
|
||||
@ -31,21 +33,22 @@ namespace Psl\Str;
|
||||
* => Bool(false)
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function ends_with_ci(string $string, string $suffix): bool
|
||||
function ends_with_ci(string $string, string $suffix, ?string $encoding = null): bool
|
||||
{
|
||||
if ($suffix === $string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$suffix_length = length($suffix);
|
||||
$total_length = length($string);
|
||||
$suffix_length = length($suffix, $encoding);
|
||||
$total_length = length($string, $encoding);
|
||||
if ($suffix_length > $total_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock - we don't supply $offset */
|
||||
$position = search_last_ci($string, $suffix);
|
||||
$position = search_last_ci($string, $suffix, 0, $encoding);
|
||||
if (null === $position) {
|
||||
return false;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
/**
|
||||
@ -15,12 +16,14 @@ use Psl\Internal;
|
||||
* => Str('ss')
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function fold(string $str): string
|
||||
function fold(string $str, ?string $encoding = null): string
|
||||
{
|
||||
foreach (Internal\CASE_FOLD as $k => $v) {
|
||||
$str = replace($str, $k, $v);
|
||||
$str = replace($str, $k, $v, $encoding);
|
||||
}
|
||||
|
||||
return lowercase($str);
|
||||
return lowercase($str, $encoding);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use function vsprintf;
|
||||
|
||||
/**
|
||||
* Return a formatted string.
|
||||
*
|
||||
@ -35,5 +37,5 @@ namespace Psl\Str;
|
||||
*/
|
||||
function format(string $format, ...$args): string
|
||||
{
|
||||
return \vsprintf($format, $args);
|
||||
return vsprintf($format, $args);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use function number_format;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the given number with grouped thousands.
|
||||
*
|
||||
@ -19,5 +21,5 @@ function format_number(
|
||||
string $decimal_point = '.',
|
||||
string $thousands_separator = ','
|
||||
): string {
|
||||
return \number_format($number, $decimals, $decimal_point, $thousands_separator);
|
||||
return number_format($number, $decimals, $decimal_point, $thousands_separator);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use function implode;
|
||||
|
||||
/**
|
||||
* Join array elements with a string.
|
||||
*
|
||||
@ -24,5 +26,5 @@ namespace Psl\Str;
|
||||
*/
|
||||
function join(array $pieces, string $glue): string
|
||||
{
|
||||
return \implode($glue, $pieces);
|
||||
return implode($glue, $pieces);
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strlen;
|
||||
|
||||
/**
|
||||
* Returns the length of the given string, i.e. the number of bytes.
|
||||
*
|
||||
@ -19,8 +24,10 @@ namespace Psl\Str;
|
||||
* => Int(4)
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function length(string $str): int
|
||||
function length(string $str, ?string $encoding = null): int
|
||||
{
|
||||
return \mb_strlen($str, encoding($str));
|
||||
return mb_strlen($str, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strtolower;
|
||||
|
||||
/**
|
||||
* Returns the string with all alphabetic characters converted to lowercase.
|
||||
*
|
||||
@ -20,12 +25,14 @@ namespace Psl\Str;
|
||||
* Str\lowercase('1337')
|
||||
* => Str('1337')
|
||||
*
|
||||
* Str\contains('سيف')
|
||||
* Str\lowercase('سيف')
|
||||
* => Str('سيف')
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function lowercase(string $lowercase): string
|
||||
function lowercase(string $lowercase, ?string $encoding = null): string
|
||||
{
|
||||
return \mb_strtolower($lowercase, encoding($lowercase));
|
||||
return mb_strtolower($lowercase, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_ord;
|
||||
|
||||
/**
|
||||
@ -18,8 +21,10 @@ use function mb_ord;
|
||||
* => Int(1604)
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function ord(string $char): int
|
||||
function ord(string $char, ?string $encoding = null): int
|
||||
{
|
||||
return mb_ord($char, encoding($char));
|
||||
return mb_ord($char, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -31,16 +31,17 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $pad_string is empty, or a negative $total_length is given.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function pad_left(string $string, int $total_length, string $pad_string = ' '): string
|
||||
function pad_left(string $string, int $total_length, string $pad_string = ' ', ?string $encoding = null): string
|
||||
{
|
||||
Psl\invariant('' !== $pad_string, 'Expected a non-empty pad string.');
|
||||
Psl\invariant($total_length >= 0, 'Expected a non-negative total length.');
|
||||
|
||||
while (length($string) < $total_length) {
|
||||
$remaining = $total_length - length($string);
|
||||
if ($remaining <= length($pad_string)) {
|
||||
$pad_string = slice($pad_string, 0, $remaining);
|
||||
while (length($string, $encoding) < $total_length) {
|
||||
$remaining = $total_length - length($string, $encoding);
|
||||
if ($remaining <= length($pad_string, $encoding)) {
|
||||
$pad_string = slice($pad_string, 0, $remaining, $encoding);
|
||||
}
|
||||
|
||||
$string = $pad_string . $string;
|
||||
|
@ -31,16 +31,17 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $pad_string is empty, or a negative $total_length is given.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function pad_right(string $string, int $total_length, string $pad_string = ' '): string
|
||||
function pad_right(string $string, int $total_length, string $pad_string = ' ', ?string $encoding = null): string
|
||||
{
|
||||
Psl\invariant('' !== $pad_string, 'Expected a non-empty pad string.');
|
||||
Psl\invariant($total_length >= 0, 'Expected a non-negative total length.');
|
||||
|
||||
while (length($string) < $total_length) {
|
||||
$remaining = $total_length - length($string);
|
||||
if ($remaining <= length($pad_string)) {
|
||||
$pad_string = slice($pad_string, 0, $remaining);
|
||||
while (length($string, $encoding) < $total_length) {
|
||||
$remaining = $total_length - length($string, $encoding);
|
||||
if ($remaining <= length($pad_string, $encoding)) {
|
||||
$pad_string = slice($pad_string, 0, $remaining, $encoding);
|
||||
}
|
||||
|
||||
$string .= $pad_string;
|
||||
|
@ -6,6 +6,8 @@ namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
use function str_repeat;
|
||||
|
||||
/**
|
||||
* Returns the input string repeated `$multiplier` times.
|
||||
*
|
||||
@ -27,5 +29,5 @@ function repeat(string $string, int $multiplier): string
|
||||
{
|
||||
Psl\invariant($multiplier >= 0, 'Expected a non-negative multiplier');
|
||||
|
||||
return \str_repeat($string, $multiplier);
|
||||
return str_repeat($string, $multiplier);
|
||||
}
|
||||
|
@ -4,15 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns the 'haystack' string with all occurrences of `$needle` replaced by
|
||||
* `$replacement`.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function replace(string $haystack, string $needle, string $replacement): string
|
||||
function replace(string $haystack, string $needle, string $replacement, ?string $encoding = null): string
|
||||
{
|
||||
if ('' === $needle || null === search($haystack, $needle)) {
|
||||
if ('' === $needle || null === search($haystack, $needle, 0, $encoding)) {
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
use function preg_quote;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Returns the 'haystack' string with all occurrences of `$needle` replaced by
|
||||
* Returns the '$haystack' string with all occurrences of `$needle` replaced by
|
||||
* `$replacement` (case-insensitive).
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function replace_ci(string $haystack, string $needle, string $replacement): string
|
||||
function replace_ci(string $haystack, string $needle, string $replacement, ?string $encoding = null): string
|
||||
{
|
||||
if ('' === $needle || null === search_ci($haystack, $needle)) {
|
||||
if ('' === $needle || null === search_ci($haystack, $needle, 0, $encoding)) {
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
return implode($replacement, preg_split('{' . preg_quote($needle, '/') . '}iu', $haystack));
|
||||
return join(preg_split('{' . preg_quote($needle, '/') . '}iu', $haystack), $replacement);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns the '$haystack' string with all occurrences of the keys of
|
||||
* `$replacements` replaced by the corresponding values.
|
||||
@ -11,11 +13,13 @@ namespace Psl\Str;
|
||||
* @psalm-param array<string, string> $replacements
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function replace_every(string $haystack, array $replacements): string
|
||||
function replace_every(string $haystack, array $replacements, ?string $encoding = null): string
|
||||
{
|
||||
foreach ($replacements as $needle => $replacement) {
|
||||
$haystack = replace($haystack, $needle, $replacement);
|
||||
$haystack = replace($haystack, $needle, $replacement, $encoding);
|
||||
}
|
||||
|
||||
return $haystack;
|
||||
|
@ -13,15 +13,17 @@ use Psl;
|
||||
* @psalm-param array<string, string> $replacements
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function replace_every_ci(string $haystack, array $replacements): string
|
||||
function replace_every_ci(string $haystack, array $replacements, ?string $encoding = null): string
|
||||
{
|
||||
foreach ($replacements as $needle => $replacement) {
|
||||
if ('' === $needle || null === search_ci($haystack, $needle)) {
|
||||
if ('' === $needle || null === search_ci($haystack, $needle, 0, $encoding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$haystack = replace_ci($haystack, $needle, $replacement);
|
||||
$haystack = replace_ci($haystack, $needle, $replacement, $encoding);
|
||||
}
|
||||
|
||||
return $haystack;
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strpos;
|
||||
|
||||
/**
|
||||
* Returns the first position of the 'needle' string in the 'haystack' string,
|
||||
@ -17,14 +20,17 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function search(string $haystack, string $needle, int $offset = 0): ?int
|
||||
function search(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): ?int
|
||||
{
|
||||
if ('' === $needle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$offset = Psl\Internal\validate_offset($offset, length($haystack));
|
||||
$offset = Psl\Internal\validate_offset($offset, length($haystack, $encoding));
|
||||
|
||||
return false === ($pos = \mb_strpos($haystack, $needle, $offset, encoding($haystack))) ? null : $pos;
|
||||
return false === ($pos = mb_strpos($haystack, $needle, $offset, Internal\internal_encoding($encoding))) ?
|
||||
null :
|
||||
$pos;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_stripos;
|
||||
|
||||
/**
|
||||
* Returns the first position of the 'needle' string in the 'haystack' string,
|
||||
@ -17,14 +20,17 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If $offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function search_ci(string $haystack, string $needle, int $offset = 0): ?int
|
||||
function search_ci(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): ?int
|
||||
{
|
||||
if ('' === $needle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$offset = Psl\Internal\validate_offset($offset, length($haystack));
|
||||
$offset = Psl\Internal\validate_offset($offset, length($haystack, $encoding));
|
||||
|
||||
return false === ($pos = \mb_stripos($haystack, $needle, $offset, encoding($haystack))) ? null : $pos;
|
||||
return false === ($pos = mb_stripos($haystack, $needle, $offset, Internal\internal_encoding($encoding))) ?
|
||||
null :
|
||||
$pos;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strrpos;
|
||||
|
||||
/**
|
||||
* Returns the last position of the 'needle' string in the 'haystack' string,
|
||||
@ -17,15 +20,18 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the $offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function search_last(string $haystack, string $needle, int $offset = 0): ?int
|
||||
function search_last(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): ?int
|
||||
{
|
||||
if ('' === $needle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$haystack_length = length($haystack);
|
||||
$haystack_length = length($haystack, $encoding);
|
||||
Psl\invariant($offset >= -$haystack_length && $offset <= $haystack_length, 'Offset is out-of-bounds.');
|
||||
|
||||
return false === ($pos = \mb_strrpos($haystack, $needle, $offset)) ? null : $pos;
|
||||
return false === ($pos = mb_strrpos($haystack, $needle, $offset, Internal\internal_encoding($encoding))) ?
|
||||
null :
|
||||
$pos;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strripos;
|
||||
|
||||
/**
|
||||
* Returns the last position of the 'needle' string in the 'haystack' string,
|
||||
@ -17,15 +20,18 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function search_last_ci(string $haystack, string $needle, int $offset = 0): ?int
|
||||
function search_last_ci(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): ?int
|
||||
{
|
||||
if ('' === $needle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$haystack_length = length($haystack);
|
||||
$haystack_length = length($haystack, $encoding);
|
||||
Psl\invariant($offset >= -$haystack_length && $offset <= $haystack_length, 'Offset is out-of-bounds.');
|
||||
|
||||
return false === ($pos = \mb_strripos($haystack, $needle, $offset)) ? null : $pos;
|
||||
return false === ($pos = mb_strripos($haystack, $needle, $offset, Internal\internal_encoding($encoding))) ?
|
||||
null :
|
||||
$pos;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_substr;
|
||||
|
||||
/**
|
||||
* Returns a substring of length `$length` of the given string starting at the
|
||||
@ -17,16 +20,17 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If a negative $length is given.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function slice(string $string, int $offset, ?int $length = null): string
|
||||
function slice(string $string, int $offset, ?int $length = null, ?string $encoding = null): string
|
||||
{
|
||||
Psl\invariant(null === $length || $length >= 0, 'Expected a non-negative length.');
|
||||
$string_length = length($string);
|
||||
$string_length = length($string, $encoding);
|
||||
$offset = Psl\Internal\validate_offset($offset, $string_length);
|
||||
|
||||
if (0 === $offset && (null === $length || $string_length <= $length)) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return false === ($result = \mb_substr($string, $offset, $length, encoding($string))) ? '' : $result;
|
||||
return (string) mb_substr($string, $offset, $length, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -17,16 +17,22 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If a negative $length is given.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function splice(string $string, string $replacement, int $offset = 0, ?int $length = null): string
|
||||
{
|
||||
function splice(
|
||||
string $string,
|
||||
string $replacement,
|
||||
int $offset = 0,
|
||||
?int $length = null,
|
||||
?string $encoding = null
|
||||
): string {
|
||||
Psl\invariant(null === $length || $length >= 0, 'Expected a non-negative length.');
|
||||
$total_length = length($string);
|
||||
$total_length = length($string, $encoding);
|
||||
$offset = Psl\Internal\validate_offset($offset, $total_length);
|
||||
|
||||
if (null === $length || ($offset + $length) >= $total_length) {
|
||||
return slice($string, 0, $offset) . $replacement;
|
||||
return slice($string, 0, $offset, $encoding) . $replacement;
|
||||
}
|
||||
|
||||
return slice($string, 0, $offset) . $replacement . slice($string, $offset + $length);
|
||||
return slice($string, 0, $offset, $encoding) . $replacement . slice($string, $offset + $length, null, $encoding);
|
||||
}
|
||||
|
@ -19,21 +19,22 @@ use Psl\Math;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If a negative $limit is given.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function split(string $string, string $delimiter, ?int $limit = null): array
|
||||
function split(string $string, string $delimiter, ?int $limit = null, ?string $encoding = null): array
|
||||
{
|
||||
Psl\invariant(null === $limit || $limit >= 1, 'Expected a non-negative limit');
|
||||
if ('' === $delimiter) {
|
||||
if (null === $limit || $limit >= length($string)) {
|
||||
return chunk($string);
|
||||
if (null === $limit || $limit >= length($string, $encoding)) {
|
||||
return chunk($string, 1, $encoding);
|
||||
}
|
||||
|
||||
if (1 === $limit) {
|
||||
return [$string];
|
||||
}
|
||||
|
||||
$result = chunk(slice($string, 0, $limit - 1));
|
||||
$result[] = slice($string, $limit - 1);
|
||||
$result = chunk(slice($string, 0, $limit - 1, $encoding), 1, $encoding);
|
||||
$result[] = slice($string, $limit - 1, null, $encoding);
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -43,14 +44,14 @@ function split(string $string, string $delimiter, ?int $limit = null): array
|
||||
$tail = $string;
|
||||
$chunks = [];
|
||||
|
||||
$position = search($tail, $delimiter);
|
||||
$position = search($tail, $delimiter, 0, $encoding);
|
||||
while (1 < $limit && null !== $position) {
|
||||
$result = slice($tail, 0, $position);
|
||||
$result = slice($tail, 0, $position, $encoding);
|
||||
$chunks[] = $result;
|
||||
$tail = slice($tail, length($result) + length($delimiter));
|
||||
$tail = slice($tail, length($result, $encoding) + length($delimiter, $encoding), null, $encoding);
|
||||
|
||||
$limit--;
|
||||
$position = search($tail, $delimiter);
|
||||
$position = search($tail, $delimiter, 0, $encoding);
|
||||
}
|
||||
|
||||
$chunks[] = $tail;
|
||||
|
@ -4,13 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns whether the string starts with the given prefix.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function starts_with(string $str, string $prefix): bool
|
||||
function starts_with(string $str, string $prefix, ?string $encoding = null): bool
|
||||
{
|
||||
/** @psalm-suppress MissingThrowsDocblock - we don't supply $offset */
|
||||
return 0 === search($str, $prefix);
|
||||
return 0 === search($str, $prefix, 0, $encoding);
|
||||
}
|
||||
|
@ -4,13 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns whether the string starts with the given prefix (case-insensitive).
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function starts_with_ci(string $str, string $prefix): bool
|
||||
function starts_with_ci(string $str, string $prefix, ?string $encoding = null): bool
|
||||
{
|
||||
/** @psalm-suppress MissingThrowsDocblock - we don't supply $offset */
|
||||
return 0 === search_ci($str, $prefix);
|
||||
return 0 === search_ci($str, $prefix, 0, $encoding);
|
||||
}
|
||||
|
@ -4,18 +4,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns the string with the given prefix removed, or the string itself if
|
||||
* it doesn't start with the prefix.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function strip_prefix(string $string, string $prefix): string
|
||||
function strip_prefix(string $string, string $prefix, ?string $encoding = null): string
|
||||
{
|
||||
if ('' === $prefix || !starts_with($string, $prefix)) {
|
||||
if ('' === $prefix || !starts_with($string, $prefix, $encoding)) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock - we don't supply $offset */
|
||||
return slice($string, length($prefix));
|
||||
return slice($string, length($prefix, $encoding), null, $encoding);
|
||||
}
|
||||
|
@ -4,18 +4,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
|
||||
/**
|
||||
* Returns the string with the given suffix removed, or the string itself if
|
||||
* it doesn't end with the suffix.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function strip_suffix(string $string, string $suffix): string
|
||||
function strip_suffix(string $string, string $suffix, ?string $encoding = null): string
|
||||
{
|
||||
if ('' === $suffix || !ends_with($string, $suffix)) {
|
||||
if ('' === $suffix || !ends_with($string, $suffix, $encoding)) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
/** @psalm-suppress MissingThrowsDocblock - we are sure that the $offset is positive */
|
||||
return slice($string, 0, length($string) - length($suffix));
|
||||
return slice($string, 0, length($string, $encoding) - length($suffix, $encoding), $encoding);
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strimwidth;
|
||||
|
||||
/**
|
||||
* Get truncated string with specified width.
|
||||
@ -21,10 +24,11 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If the offset is out-of-bounds.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function truncate(string $str, int $offset, int $width, ?string $trim_marker = null): string
|
||||
function truncate(string $str, int $offset, int $width, ?string $trim_marker = null, ?string $encoding = null): string
|
||||
{
|
||||
$offset = Psl\Internal\validate_offset($offset, length($str));
|
||||
$offset = Internal\validate_offset($offset, length($str, $encoding));
|
||||
|
||||
return mb_strimwidth($str, $offset, $width, $trim_marker ?? '', encoding($str));
|
||||
return mb_strimwidth($str, $offset, $width, $trim_marker ?? '', Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -4,12 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strtoupper;
|
||||
|
||||
/**
|
||||
* Returns the string with all alphabetic characters converted to uppercase.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function uppercase(string $string): string
|
||||
function uppercase(string $string, ?string $encoding = null): string
|
||||
{
|
||||
return \mb_strtoupper($string, encoding($string));
|
||||
return mb_strtoupper($string, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -4,12 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psl\Str;
|
||||
|
||||
use Psl;
|
||||
use Psl\Internal;
|
||||
|
||||
use function mb_strwidth;
|
||||
|
||||
/**
|
||||
* Return width of length.
|
||||
*
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function width(string $str): int
|
||||
function width(string $str, ?string $encoding = null): int
|
||||
{
|
||||
return \mb_strwidth($str, encoding($str));
|
||||
return mb_strwidth($str, Internal\internal_encoding($encoding));
|
||||
}
|
||||
|
@ -19,31 +19,35 @@ use Psl;
|
||||
* @psalm-pure
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException If $break is empty, or $width is 0 and $cut is set to true.
|
||||
* @throws Psl\Exception\InvariantViolationException If an invalid $encoding is provided.
|
||||
*/
|
||||
function wrap(string $string, int $width = 75, string $break = "\n", bool $cut = false): string
|
||||
{
|
||||
function wrap(
|
||||
string $string,
|
||||
int $width = 75,
|
||||
string $break = "\n",
|
||||
bool $cut = false,
|
||||
?string $encoding = null
|
||||
): string {
|
||||
if ('' === $string) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// if ('' === $break) -> throw
|
||||
Psl\invariant('' !== $break, 'Break string cannot be empty.');
|
||||
// if (0 === $width && $cut) -> throw
|
||||
Psl\invariant(0 !== $width || !$cut, 'Cannot force cut when width is zero.');
|
||||
|
||||
$stringLength = length($string);
|
||||
$breakLength = length($break);
|
||||
$stringLength = length($string, $encoding);
|
||||
$breakLength = length($break, $encoding);
|
||||
$result = '';
|
||||
$lastStart = $lastSpace = 0;
|
||||
for ($current = 0; $current < $stringLength; ++$current) {
|
||||
$char = slice($string, $current, 1);
|
||||
$char = slice($string, $current, 1, $encoding);
|
||||
$possibleBreak = $char;
|
||||
if (1 !== $breakLength) {
|
||||
$possibleBreak = slice($string, $current, $breakLength);
|
||||
$possibleBreak = slice($string, $current, $breakLength, $encoding);
|
||||
}
|
||||
|
||||
if ($possibleBreak === $break) {
|
||||
$result .= slice($string, $lastStart, $current - $lastStart + $breakLength);
|
||||
$result .= slice($string, $lastStart, $current - $lastStart + $breakLength, $encoding);
|
||||
$current += $breakLength - 1;
|
||||
$lastStart = $lastSpace = $current + 1;
|
||||
continue;
|
||||
@ -51,7 +55,7 @@ function wrap(string $string, int $width = 75, string $break = "\n", bool $cut =
|
||||
|
||||
if (' ' === $char) {
|
||||
if ($current - $lastStart >= $width) {
|
||||
$result .= slice($string, $lastStart, $current - $lastStart) . $break;
|
||||
$result .= slice($string, $lastStart, $current - $lastStart, $encoding) . $break;
|
||||
$lastStart = $current + 1;
|
||||
}
|
||||
$lastSpace = $current;
|
||||
@ -59,20 +63,20 @@ function wrap(string $string, int $width = 75, string $break = "\n", bool $cut =
|
||||
}
|
||||
|
||||
if ($current - $lastStart >= $width && $cut && $lastStart >= $lastSpace) {
|
||||
$result .= slice($string, $lastStart, $current - $lastStart) . $break;
|
||||
$result .= slice($string, $lastStart, $current - $lastStart, $encoding) . $break;
|
||||
$lastStart = $lastSpace = $current;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($current - $lastStart >= $width && $lastStart < $lastSpace) {
|
||||
$result .= slice($string, $lastStart, $lastSpace - $lastStart) . $break;
|
||||
$result .= slice($string, $lastStart, $lastSpace - $lastStart, $encoding) . $break;
|
||||
$lastStart = ++$lastSpace;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($lastStart !== $current) {
|
||||
$result .= slice($string, $lastStart, $current - $lastStart);
|
||||
$result .= slice($string, $lastStart, $current - $lastStart, $encoding);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
29
tests/Psl/Str/EncodingTest.php
Normal file
29
tests/Psl/Str/EncodingTest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Tests\Str;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psl\Str;
|
||||
|
||||
class EncodingTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
*/
|
||||
public function testEncoding(?string $expected, string $string): void
|
||||
{
|
||||
self::assertSame($expected, Str\encoding($string));
|
||||
}
|
||||
|
||||
public function provideData(): array
|
||||
{
|
||||
return [
|
||||
['ASCII', 'hello'],
|
||||
['UTF-8', 'سيف'],
|
||||
['UTF-8', '🐘'],
|
||||
[null, Str\Byte\chr(128)]
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user