Allow PHP-8.2 and up Compatibility instead of just PHP-8.4

This commit is contained in:
johnnyq
2026-06-12 17:06:10 -04:00
parent 2204bd52f4
commit d3a93652f3
220 changed files with 7198 additions and 2635 deletions

View File

@@ -34,42 +34,42 @@ class Str
/**
* The cache of snake-cased words.
*
* @var array
* @var array<string, string>
*/
protected static $snakeCache = [];
/**
* The cache of camel-cased words.
*
* @var array
* @var array<string, string>
*/
protected static $camelCache = [];
/**
* The cache of studly-cased words.
*
* @var array
* @var array<string, string>
*/
protected static $studlyCache = [];
/**
* The callback that should be used to generate UUIDs.
*
* @var callable|null
* @var (callable(): \Ramsey\Uuid\UuidInterface)|null
*/
protected static $uuidFactory;
/**
* The callback that should be used to generate ULIDs.
*
* @var callable|null
* @var (callable(): \Symfony\Component\Uid\Ulid)|null
*/
protected static $ulidFactory;
/**
* The callback that should be used to generate random strings.
*
* @var callable|null
* @var (callable(int): string)|null
*/
protected static $randomStringFactory;
@@ -109,13 +109,13 @@ class Str
return $subject;
}
$position = strrpos($subject, (string) $search);
$position = mb_strrpos($subject, $search);
if ($position === false) {
return $subject;
}
return substr($subject, $position + strlen($search));
return static::substr($subject, $position + static::length($search));
}
/**
@@ -221,7 +221,7 @@ class Str
* Convert a value to camel case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : string)
*/
public static function camel($value)
{
@@ -254,14 +254,14 @@ class Str
* Remove the given string(s) if it exists at the start of the haystack.
*
* @param string $subject
* @param string|array $needle
* @param string|string[] $needle
* @return string
*/
public static function chopStart($subject, $needle)
{
foreach ((array) $needle as $n) {
if (str_starts_with($subject, $n)) {
return substr($subject, strlen($n));
if ($n !== '' && str_starts_with($subject, $n)) {
return mb_substr($subject, mb_strlen($n));
}
}
@@ -272,14 +272,14 @@ class Str
* Remove the given string(s) if it exists at the end of the haystack.
*
* @param string $subject
* @param string|array $needle
* @param string|string[] $needle
* @return string
*/
public static function chopEnd($subject, $needle)
{
foreach ((array) $needle as $n) {
if (str_ends_with($subject, $n)) {
return substr($subject, 0, -strlen($n));
if ($n !== '' && str_ends_with($subject, $n)) {
return mb_substr($subject, 0, -mb_strlen($n));
}
}
@@ -292,7 +292,7 @@ class Str
* @param string $haystack
* @param string|iterable<string> $needles
* @param bool $ignoreCase
* @return bool
* @return ($needles is array{} ? false : ($haystack is non-empty-string ? bool : false))
*/
public static function contains($haystack, $needles, $ignoreCase = false)
{
@@ -327,7 +327,7 @@ class Str
* @param string $haystack
* @param iterable<string> $needles
* @param bool $ignoreCase
* @return bool
* @return ($needles is array{} ? false : ($haystack is non-empty-string ? bool : false))
*/
public static function containsAll($haystack, $needles, $ignoreCase = false)
{
@@ -346,7 +346,7 @@ class Str
* @param string $haystack
* @param string|iterable<string> $needles
* @param bool $ignoreCase
* @return bool
* @return ($needles is array{} ? true : ($haystack is non-empty-string ? bool : true))
*/
public static function doesntContain($haystack, $needles, $ignoreCase = false)
{
@@ -357,9 +357,9 @@ class Str
* Convert the case of a string.
*
* @param string $string
* @param int $mode
* @param MB_CASE_UPPER|MB_CASE_LOWER|MB_CASE_TITLE|MB_CASE_FOLD|MB_CASE_UPPER_SIMPLE|MB_CASE_LOWER_SIMPLE|MB_CASE_TITLE_SIMPLE|MB_CASE_FOLD_SIMPLE $mode
* @param string|null $encoding
* @return string
* @return ($string is '' ? '' : string)
*/
public static function convertCase(string $string, int $mode = MB_CASE_FOLD, ?string $encoding = 'UTF-8')
{
@@ -371,7 +371,7 @@ class Str
*
* @param string $string
* @param array<string>|string $characters
* @return string
* @return ($string is '' ? '' : string)
*/
public static function deduplicate(string $string, array|string $characters = ' ')
{
@@ -391,7 +391,7 @@ class Str
*
* @param string $haystack
* @param string|iterable<string> $needles
* @return bool
* @return ($needles is array{} ? false : ($haystack is non-empty-string ? bool : false))
*/
public static function endsWith($haystack, $needles)
{
@@ -417,7 +417,7 @@ class Str
*
* @param string $haystack
* @param string|iterable<string> $needles
* @return bool
* @return ($needles is array{} ? true : ($haystack is non-empty-string ? bool : true))
*/
public static function doesntEndWith($haystack, $needles)
{
@@ -429,7 +429,7 @@ class Str
*
* @param string $text
* @param string $phrase
* @param array $options
* @param array{radius?: int|float, omission?: string} $options
* @return string|null
*/
public static function excerpt($text, $phrase = '', $options = [])
@@ -465,7 +465,7 @@ class Str
*
* @param string $value
* @param string $cap
* @return string
* @return ($value is '' ? ($cap is '' ? '' : non-empty-string) : non-empty-string)
*/
public static function finish($value, $cap)
{
@@ -480,7 +480,7 @@ class Str
* @param string $value
* @param string $before
* @param string|null $after
* @return string
* @return ($value is '' ? ($before is '' ? ($after is '' ? '' : ($after is null ? '' : non-empty-string)) : non-empty-string) : non-empty-string)
*/
public static function wrap($value, $before, $after = null)
{
@@ -569,6 +569,8 @@ class Str
*
* @param mixed $value
* @return bool
*
* @phpstan-assert-if-true =non-empty-string $value
*/
public static function isJson($value)
{
@@ -583,8 +585,10 @@ class Str
* Determine if a given value is a valid URL.
*
* @param mixed $value
* @param array $protocols
* @param string[] $protocols
* @return bool
*
* @phpstan-assert-if-true =non-empty-string $value
*/
public static function isUrl($value, array $protocols = [])
{
@@ -605,10 +609,21 @@ class Str
(LARAVEL_PROTOCOLS):// # protocol
(((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+)@)? # basic auth
(
([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
| # or
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
| # or
(?:
(?:
(?:[\pL\pN\pS\pM\-\_]++\.)+
(?:
(?:xn--[a-z0-9-]++) # punycode in tld
|
(?:[\pL\pN\pM]++) # no punycode in tld
)
) # a multi-level domain name
|
[a-z0-9\-\_]++ # a single-level domain name
)\.?
| # or
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
| # or
\[
(?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
\] # an IPv6 address
@@ -628,6 +643,8 @@ class Str
* @param mixed $value
* @param int<0, 8>|'nil'|'max'|null $version
* @return bool
*
* @phpstan-assert-if-true =non-empty-string $value
*/
public static function isUuid($value, $version = null)
{
@@ -669,6 +686,8 @@ class Str
*
* @param mixed $value
* @return bool
*
* @phpstan-assert-if-true =non-empty-string $value
*/
public static function isUlid($value)
{
@@ -683,7 +702,7 @@ class Str
* Convert a string to kebab case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : string)
*/
public static function kebab($value)
{
@@ -695,7 +714,7 @@ class Str
*
* @param string $value
* @param string|null $encoding
* @return int
* @return non-negative-int
*/
public static function length($value, $encoding = null)
{
@@ -736,7 +755,7 @@ class Str
* Convert the given string to lower-case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : non-empty-string&lowercase-string)
*/
public static function lower($value)
{
@@ -767,8 +786,8 @@ class Str
*
* @param string $string
* @param array $options
* @param array $extensions
* @return string
* @param \League\CommonMark\Extension\ExtensionInterface[] $extensions
* @return ($string is '' ? '' : string)
*/
public static function markdown($string, array $options = [], array $extensions = [])
{
@@ -788,8 +807,8 @@ class Str
*
* @param string $string
* @param array $options
* @param array $extensions
* @return string
* @param \League\CommonMark\Extension\ExtensionInterface[] $extensions
* @return ($string is '' ? '' : string)
*/
public static function inlineMarkdown($string, array $options = [], array $extensions = [])
{
@@ -866,7 +885,7 @@ class Str
*
* @param string|iterable<string> $pattern
* @param string $value
* @return bool
* @return ($pattern is array{} ? false : bool)
*/
public static function isMatch($pattern, $value)
{
@@ -988,6 +1007,10 @@ class Str
*/
public static function plural($value, $count = 2, $prependCount = false)
{
if (is_countable($count)) {
$count = count($count);
}
return ($prependCount ? Number::format($count).' ' : '').Pluralizer::plural($value, $count);
}
@@ -1027,7 +1050,7 @@ class Str
* @param bool $numbers
* @param bool $symbols
* @param bool $spaces
* @return string
* @return ($letters is false ? ($numbers is true ? ($symbols is false ? ($spaces is false ? numeric-string : string) : string) : string) : string)
*/
public static function password($length = 32, $letters = true, $numbers = true, $symbols = true, $spaces = false)
{
@@ -1069,7 +1092,7 @@ class Str
* @param string $needle
* @param int $offset
* @param string|null $encoding
* @return int|false
* @return ($haystack is '' ? false : ($needle is '' ? false : int|false))
*/
public static function position($haystack, $needle, $offset = 0, $encoding = null)
{
@@ -1104,7 +1127,7 @@ class Str
/**
* Set the callable that will be used to generate random strings.
*
* @param callable|null $factory
* @param (callable(int): string)|null $factory
* @return void
*/
public static function createRandomStringsUsing(?callable $factory = null)
@@ -1115,8 +1138,8 @@ class Str
/**
* Set the sequence that will be used to generate random strings.
*
* @param array $sequence
* @param callable|null $whenMissing
* @param string[] $sequence
* @param (callable(int): string)|null $whenMissing
* @return void
*/
public static function createRandomStringsUsingSequence(array $sequence, $whenMissing = null)
@@ -1216,7 +1239,7 @@ class Str
* @param string|iterable<string> $replace
* @param string|iterable<string> $subject
* @param bool $caseSensitive
* @return string|string[]
* @return ($subject is string ? string : string[])
*/
public static function replace($search, $replace, $subject, $caseSensitive = true)
{
@@ -1336,11 +1359,11 @@ class Str
/**
* Replace the patterns matching the given regular expression.
*
* @param array|string $pattern
* @param \Closure|string[]|string $replace
* @param array|string $subject
* @param string|string[] $pattern
* @param (\Closure(array): string)|string[]|string $replace
* @param string[]|string $subject
* @param int $limit
* @return string|string[]|null
* @return ($subject is array ? string[]|null : string|null)
*/
public static function replaceMatches($pattern, $replace, $subject, $limit = -1)
{
@@ -1386,7 +1409,7 @@ class Str
*
* @param string $value
* @param string $prefix
* @return string
* @return ($value is '' ? ($prefix is '' ? '' : non-empty-string): non-empty-string)
*/
public static function start($value, $prefix)
{
@@ -1399,7 +1422,7 @@ class Str
* Convert the given string to upper-case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : non-empty-string&uppercase-string)
*/
public static function upper($value)
{
@@ -1436,6 +1459,24 @@ class Str
return implode(' ', array_filter(explode('_', $collapsed)));
}
/**
* Get the "initials" representing each word in the provided string, optionally capitalizing.
*
* @param string $value
* @param bool $capitalize
* @return string
*/
public static function initials($value, $capitalize = false)
{
$parts = mb_split("\s+", $value);
$parts = array_map(fn ($part) => mb_substr($part, 0, 1), $parts);
$initials = implode('', $parts);
return $capitalize ? static::upper($initials) : $initials;
}
/**
* Convert the given string to APA-style title case.
*
@@ -1452,7 +1493,7 @@ class Str
$minorWords = [
'and', 'as', 'but', 'for', 'if', 'nor', 'or', 'so', 'yet', 'a', 'an',
'the', 'at', 'by', 'for', 'in', 'of', 'off', 'on', 'per', 'to', 'up', 'via',
'the', 'at', 'by', 'in', 'of', 'off', 'on', 'per', 'to', 'up', 'via',
'et', 'ou', 'un', 'une', 'la', 'le', 'les', 'de', 'du', 'des', 'par', 'à',
];
@@ -1627,7 +1668,9 @@ class Str
*
* @param string $haystack
* @param string|iterable<string> $needles
* @return bool
* @return ($needles is array{} ? false : ($haystack is non-empty-string ? bool : false))
*
* @phpstan-assert-if-true =non-empty-string $haystack
*/
public static function startsWith($haystack, $needles)
{
@@ -1653,7 +1696,9 @@ class Str
*
* @param string $haystack
* @param string|iterable<string> $needles
* @return bool
* @return ($needles is array{} ? true : ($haystack is non-empty-string ? bool : true))
*
* @phpstan-assert-if-false =non-empty-string $haystack
*/
public static function doesntStartWith($haystack, $needles)
{
@@ -1664,7 +1709,7 @@ class Str
* Convert a value to studly caps case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : string)
*/
public static function studly($value)
{
@@ -1685,7 +1730,7 @@ class Str
* Convert a value to Pascal case.
*
* @param string $value
* @return string
* @return ($value is '' ? '' : string)
*/
public static function pascal($value)
{
@@ -1736,16 +1781,18 @@ class Str
public static function substrReplace($string, $replace, $offset = 0, $length = null)
{
if ($length === null) {
$length = strlen($string);
$length = static::length($string);
}
return substr_replace($string, $replace, $offset, $length);
return mb_substr($string, 0, $offset)
.$replace
.mb_substr(mb_substr($string, $offset), $length);
}
/**
* Swap multiple keywords in a string with other keywords.
*
* @param array $map
* @param array<string, string> $map
* @param string $subject
* @return string
*/
@@ -1774,7 +1821,7 @@ class Str
* Convert the given string to Base64 encoding.
*
* @param string $string
* @return string
* @return ($string is '' ? '' : string)
*/
public static function toBase64($string): string
{
@@ -1786,7 +1833,7 @@ class Str
*
* @param string $string
* @param bool $strict
* @return string|false
* @return ($strict is true ? ($string is '' ? '' : string|false) : ($string is '' ? '' : string))
*/
public static function fromBase64($string, $strict = false)
{
@@ -1797,7 +1844,7 @@ class Str
* Make a string's first character lowercase.
*
* @param string $string
* @return string
* @return ($string is '' ? '' : non-empty-string)
*/
public static function lcfirst($string)
{
@@ -1808,18 +1855,34 @@ class Str
* Make a string's first character uppercase.
*
* @param string $string
* @return string
* @return ($string is '' ? '' : non-empty-string)
*/
public static function ucfirst($string)
{
return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
}
/**
* Capitalize the first character of each word in a string.
*
* @param string $string
* @param string $separators
* @return ($string is '' ? '' : non-empty-string)
*/
public static function ucwords($string, $separators = " \t\r\n\f\v")
{
$pattern = '/(^|['.preg_quote($separators, '/').'])(\p{Ll})/u';
return preg_replace_callback($pattern, function ($matches) {
return $matches[1].mb_strtoupper($matches[2]);
}, $string);
}
/**
* Split a string into pieces by uppercase characters.
*
* @param string $string
* @return string[]
* @return ($string is '' ? array{} : string[])
*/
public static function ucsplit($string)
{
@@ -1831,7 +1894,7 @@ class Str
*
* @param string $string
* @param string|null $characters
* @return int
* @return non-negative-int
*/
public static function wordCount($string, $characters = null)
{
@@ -1905,7 +1968,7 @@ class Str
/**
* Set the callable that will be used to generate UUIDs.
*
* @param callable|null $factory
* @param (callable(): \Ramsey\Uuid\UuidInterface)|null $factory
* @return void
*/
public static function createUuidsUsing(?callable $factory = null)
@@ -1916,8 +1979,8 @@ class Str
/**
* Set the sequence that will be used to generate UUIDs.
*
* @param array $sequence
* @param callable|null $whenMissing
* @param \Ramsey\Uuid\UuidInterface[] $sequence
* @param (callable(): \Ramsey\Uuid\UuidInterface)|null $whenMissing
* @return void
*/
public static function createUuidsUsingSequence(array $sequence, $whenMissing = null)
@@ -1950,7 +2013,7 @@ class Str
/**
* Always return the same UUID when generating new UUIDs.
*
* @param \Closure|null $callback
* @param (\Closure(\Ramsey\Uuid\UuidInterface): mixed)|null $callback
* @return \Ramsey\Uuid\UuidInterface
*/
public static function freezeUuids(?Closure $callback = null)
@@ -2012,7 +2075,7 @@ class Str
/**
* Set the callable that will be used to generate ULIDs.
*
* @param callable|null $factory
* @param (callable(): \Symfony\Component\Uid\Ulid)|null $factory
* @return void
*/
public static function createUlidsUsing(?callable $factory = null)
@@ -2023,8 +2086,8 @@ class Str
/**
* Set the sequence that will be used to generate ULIDs.
*
* @param array $sequence
* @param callable|null $whenMissing
* @param \Symfony\Component\Uid\Ulid[] $sequence
* @param (callable(): \Symfony\Component\Uid\Ulid)|null $whenMissing
* @return void
*/
public static function createUlidsUsingSequence(array $sequence, $whenMissing = null)
@@ -2057,7 +2120,7 @@ class Str
/**
* Always return the same ULID when generating new ULIDs.
*
* @param Closure|null $callback
* @param (Closure(Ulid): mixed)|null $callback
* @return Ulid
*/
public static function freezeUlids(?Closure $callback = null)