.gitattributes000064400000000054151360551400007436 0ustar00/tests export-ignore /.github export-ignore LICENSE000064400000002110151360551400005543 0ustar00The MIT License (MIT) Copyright (c) Taylor Otwell Copyright (c) Hyperf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. composer.json000064400000002620151360551400007266 0ustar00{ "name": "hyperf/stringable", "description": "Hyperf Stringable package which come from illuminate/support", "license": "MIT", "keywords": [ "php", "swoole", "hyperf", "stringable" ], "homepage": "https://hyperf.io", "support": { "issues": "https://github.com/hyperf/hyperf/issues", "source": "https://github.com/hyperf/hyperf", "docs": "https://hyperf.wiki", "pull-request": "https://github.com/hyperf/hyperf/pulls" }, "require": { "php": ">=8.1", "ext-mbstring": "*", "hyperf/collection": "~3.1.0", "hyperf/conditionable": "~3.1.0", "hyperf/macroable": "~3.1.0", "hyperf/tappable": "~3.1.0" }, "suggest": { "doctrine/inflector": "Required to use plural and singular methods.(^2.0|^3.0)", "ramsey/uuid": "Required to use uuid and orderedUuid methods.(^4.7|^5.0)", "symfony/uid": "Required to use ulid method.(^5.0|^6.0)" }, "autoload": { "psr-4": { "Hyperf\\Stringable\\": "src/" }, "files": [ "src/Functions.php" ] }, "autoload-dev": { "psr-4": { "HyperfTest\\Stringable\\": "tests/" } }, "config": { "sort-packages": true }, "extra": { "branch-alias": { "dev-master": "3.1-dev" } } } src/Functions.php000064400000001436151360551400010020 0ustar00pluralize($value); return static::matchCase($plural, $value); } /** * Get the singular form of an English word. */ public static function singular(string $value): string { $singular = static::getInflector()->singularize($value); return static::matchCase($singular, $value); } public static function setInflector(?Inflector $inflector): void { static::$inflector = $inflector; } /** * Get the inflector instance. */ public static function getInflector(): Inflector { if (is_null(static::$inflector)) { static::$inflector = InflectorFactory::createForLanguage(static::$language)->build(); } return static::$inflector; } public static function useLanguage(string $language): void { static::$language = $language; static::$inflector = null; } /** * Determine if the given value is uncountable. * * @param string $value * @return bool */ protected static function uncountable($value) { return in_array(strtolower($value), static::$uncountable); } /** * Attempt to match the case on two strings. */ protected static function matchCase(string $value, string $comparison): string { $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords']; foreach ($functions as $function) { if ($function($comparison) === $comparison) { return $function($value); } } return $value; } } src/Str.php000064400000165176151360551400006634 0ustar00 $val) { $value = str_replace($val, (string) $key, $value); } return preg_replace('/[^\x20-\x7E]/u', '', $value); } /** * Get the portion of a string before a given value. * * @param string $subject * @param string $search * @return string */ public static function before($subject, $search) { return $search === '' ? $subject : explode($search, $subject)[0]; } /** * Get the portion of a string before the last occurrence of a given value. * * @param string $subject * @param string $search * @return string */ public static function beforeLast($subject, $search) { if ($search === '') { return $subject; } $pos = mb_strrpos($subject, $search); if ($pos === false) { return $subject; } return static::substr($subject, 0, $pos); } /** * Get the portion of a string between two given values. * * @param string $subject * @param string $from * @param string $to * @return string */ public static function between($subject, $from, $to) { if ($from === '' || $to === '') { return $subject; } return static::beforeLast(static::after($subject, $from), $to); } /** * Convert a value to camel case. * * @param string $value * @return string */ public static function camel($value) { return lcfirst(static::studly($value)); } /** * Get the character at the specified index. * * @param string $subject * @param int $index * @return null|string */ public static function charAt($subject, $index) { $length = mb_strlen($subject); if ($index < 0 ? $index < -$length : $index > $length - 1) { return null; } return mb_substr($subject, $index, 1); } /** * Remove the given string(s) if it exists at the end of the haystack. * * @param string $subject * @param array|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)); } } return $subject; } /** * Remove the given string(s) if it exists at the start of the haystack. * * @param string $subject * @param array|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)); } } return $subject; } /** * Determine if a given string contains a given substring. * * @param array|string $needles */ public static function contains(string $haystack, mixed $needles, bool $ignoreCase = false): bool { if ($ignoreCase) { return static::containsIgnoreCase($haystack, $needles); } foreach ((array) $needles as $needle) { $needle = (string) $needle; if ($needle !== '' && str_contains($haystack, $needle)) { return true; } } return false; } /** * Determine if a given string contains a given substring regardless of case sensitivity. * * @param array|string $needles */ public static function containsIgnoreCase(string $haystack, $needles): bool { foreach ((array) $needles as $needle) { $needle = (string) $needle; if ($needle !== '' && stripos($haystack, $needle) !== false) { return true; } } return false; } /** * Determine if a given string contains all array values. * * @param string[] $needles * @return bool */ public static function containsAll(string $haystack, array $needles, bool $ignoreCase = false) { foreach ($needles as $needle) { if (! static::contains($haystack, $needle, $ignoreCase)) { return false; } } return true; } /** * Determine if a given string ends with a given substring. * * @param array|string $needles * @return bool */ public static function endsWith(string $haystack, $needles) { foreach ((array) $needles as $needle) { $needle = (string) $needle; if ($needle !== '' && str_ends_with($haystack, $needle)) { return true; } } return false; } /** * Cap a string with a single instance of a given value. * * @param string $value * @param string $cap * @return string */ public static function finish($value, $cap) { $quoted = preg_quote($cap, '/'); return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap; } /** * Determine if a given string matches a given pattern. * * @param array|string $pattern * @param string $value * @return bool */ public static function is($pattern, $value) { $value = (string) $value; $patterns = Arr::wrap($pattern); if (empty($patterns)) { return false; } foreach ($patterns as $pattern) { $pattern = (string) $pattern; // If the given value is an exact match we can of course return true right // from the beginning. Otherwise, we will translate asterisks and do an // actual pattern match against the two strings to see if they match. if ($pattern == $value) { return true; } $pattern = preg_quote($pattern, '#'); // Asterisks are translated into zero-or-more regular expression wildcards // to make it convenient to check if the strings starts with the given // pattern such as "library/*", making any string check convenient. $pattern = str_replace('\*', '.*', $pattern); if (preg_match('#^' . $pattern . '\z#u', $value) === 1) { return true; } } return false; } /** * Determine if a given string is 7 bit ASCII. */ public static function isAscii(string $value): bool { if ($value == '') { return true; } return ! preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $value); } /** * Convert a string to kebab case. * * @param string $value * @return string */ public static function kebab($value) { return static::snake($value, '-'); } /** * Return the length of the given string. * * @param string $value * @param string $encoding * @return int */ public static function length($value, $encoding = null) { return mb_strlen($value, $encoding); } /** * Limit the number of characters in a string. * * @param string $value * @param int $limit * @param string $end * @return string */ public static function limit($value, $limit = 100, $end = '...') { if (mb_strwidth($value, 'UTF-8') <= $limit) { return $value; } return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end; } /** * Convert the given string to lower-case. * * @param string $value * @return string */ public static function lower($value) { return mb_strtolower($value, 'UTF-8'); } /** * Limit the number of words in a string. */ public static function words(string $value, int $words = 100, string $end = '...'): string { preg_match('/^\s*+(?:\S++\s*+){1,' . $words . '}/u', $value, $matches); if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) { return $value; } return rtrim($matches[0]) . $end; } /** * Get the string matching the given pattern. * * @param string $pattern * @param string $subject * @return string */ public static function match($pattern, $subject) { preg_match($pattern, $subject, $matches); if (! $matches) { return ''; } return $matches[1] ?? $matches[0]; } /** * Determine if a given string matches a given pattern. * * @param iterable|string $patterns * @param string $value * @return bool */ public static function isMatch($patterns, $value) { $value = (string) $value; if (! is_iterable($patterns)) { $patterns = [$patterns]; } foreach ($patterns as $pattern) { $pattern = (string) $pattern; if (preg_match($pattern, $value) === 1) { return true; } } return false; } /** * Get the string matching the given pattern. * * @param string $pattern * @param string $subject * @return Collection */ public static function matchAll($pattern, $subject) { preg_match_all($pattern, $subject, $matches); if (empty($matches[0])) { return collect(); } return collect($matches[1] ?? $matches[0]); } /** * Pad both sides of a string with another. * * @param string $value * @param int $length * @param string $pad * @return string */ public static function padBoth($value, $length, $pad = ' ') { return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_BOTH); } /** * Pad the left side of a string with another. * * @param string $value * @param int $length * @param string $pad * @return string */ public static function padLeft($value, $length, $pad = ' ') { return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_LEFT); } /** * Pad the right side of a string with another. * * @param string $value * @param int $length * @param string $pad * @return string */ public static function padRight($value, $length, $pad = ' ') { return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_RIGHT); } /** * Parse a Class@method style callback into class and method. * * @param null|string $default */ public static function parseCallback(string $callback, $default = null): array { return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default]; } /** * Pluralize the last word of an English, studly caps case string. * @param mixed $count */ public static function pluralStudly(string $value, $count = 2): string { $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE); $lastWord = array_pop($parts); return implode('', $parts) . self::plural($lastWord, $count); } /** * Get the plural form of an English word. */ public static function plural(string $value, array|Countable|int $count = 2): string { return Pluralizer::plural($value, $count); } /** * Find the multi-byte safe position of the first occurrence of a given substring in a string. */ public static function position(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): false|int { return mb_strpos($haystack, (string) $needle, $offset, $encoding); } /** * Generate a more truly "random" alpha-numeric string. */ public static function random(int $length = 16): string { $string = ''; while (($len = strlen($string)) < $length) { $size = $length - $len; $bytes = random_bytes($size); $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); } return $string; } /** * Repeat the given string. * * @return string */ public static function repeat(string $string, int $times) { return str_repeat($string, $times); } /** * Replace a given value in the string sequentially with an array. * * @param string[] $replace */ public static function replaceArray(string $search, array $replace, string $subject): string { foreach ($replace as $value) { $subject = static::replaceFirst($search, (string) $value, $subject); } return $subject; } /** * Replace the given value in the given string. * * @param string|string[] $search * @param string|string[] $replace * @param string|string[] $subject * @return string */ public static function replace($search, $replace, $subject) { return str_replace($search, $replace, $subject); } /** * Replace the first occurrence of a given value in the string. */ public static function replaceFirst(string $search, string $replace, string $subject): string { if ($search == '') { return $subject; } $position = strpos($subject, $search); if ($position !== false) { return substr_replace($subject, $replace, $position, strlen($search)); } return $subject; } /** * Replace the last occurrence of a given value in the string. */ public static function replaceLast(string $search, string $replace, string $subject): string { if ($search == '') { return $subject; } $position = strrpos($subject, $search); if ($position !== false) { return substr_replace($subject, $replace, $position, strlen($search)); } return $subject; } /** * Remove any occurrence of the given string in the subject. * * @param array|string $search * @param string $subject * @param bool $caseSensitive * @return string */ public static function remove($search, $subject, $caseSensitive = true) { return $caseSensitive ? str_replace($search, '', $subject) : str_ireplace($search, '', $subject); } /** * Begin a string with a single instance of a given value. */ public static function start(string $value, string $prefix): string { $quoted = preg_quote($prefix, '/'); return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value); } /** * Strip HTML and PHP tags from the given string. * * @param null|string|string[] $allowedTags */ public static function stripTags(string $value, $allowedTags = null): string { return strip_tags($value, $allowedTags); } /** * Convert the given string to upper-case. */ public static function upper(string $value): string { return mb_strtoupper($value, 'UTF-8'); } /** * Convert the given string to title case. */ public static function title(string $value): string { return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); } /** * Convert the given string to proper case for each word. */ public static function headline(string $value): string { $parts = explode(' ', $value); $parts = count($parts) > 1 ? array_map([static::class, 'title'], $parts) : array_map([static::class, 'title'], static::ucsplit(implode('_', $parts))); $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts)); return implode(' ', array_filter(explode('_', $collapsed))); } /** * Get the singular form of an English word. */ public static function singular(string $value): string { return Pluralizer::singular($value); } /** * Generate a URL friendly "slug" from a given string. * @param mixed $dictionary */ public static function slug(string $title, string $separator = '-', ?string $language = 'en', $dictionary = ['@' => 'at']): string { $title = $language ? static::ascii($title, $language) : $title; // Convert all dashes/underscores into separator $flip = $separator === '-' ? '_' : '-'; $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title); // Replace dictionary words foreach ($dictionary as $key => $value) { $dictionary[$key] = $separator . $value . $separator; } $title = str_replace(array_keys($dictionary), array_values($dictionary), $title); // Remove all characters that are not the separator, letters, numbers, or whitespace. $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', mb_strtolower($title)); // Replace all separator characters and whitespace by a single separator $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title); return trim($title, $separator); } /** * Convert a string to snake case. */ public static function snake(string $value, string $delimiter = '_'): string { if (! ctype_lower($value)) { $value = preg_replace('/\s+/u', '', ucwords($value)); $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value)); } return $value; } /** * Determine if a given string starts with a given substring. * * @param array|string $needles */ public static function startsWith(string $haystack, $needles): bool { foreach ((array) $needles as $needle) { $needle = (string) $needle; if ($needle !== '' && str_starts_with($haystack, $needle)) { return true; } } return false; } /** * Convert a value to studly caps case. */ public static function studly(string $value, string $gap = ''): string { $value = ucwords(str_replace(['-', '_'], ' ', $value)); return str_replace(' ', $gap, $value); } /** * Returns the portion of string specified by the start and length parameters. * * @param string $string * @param int $start * @param null|int $length * @return string */ public static function substr($string, $start, $length = null) { return mb_substr($string, $start, $length, 'UTF-8'); } /** * Returns the number of substring occurrences. * * @param string $haystack * @param string $needle * @param int $offset * @param null|int $length * @return int */ public static function substrCount($haystack, $needle, $offset = 0, $length = null) { if (! is_null($length)) { return substr_count($haystack, $needle, $offset, $length); } return substr_count($haystack, $needle, $offset); } /** * Make a string's first character uppercase. */ public static function ucfirst(string $string): string { return static::upper(static::substr($string, 0, 1)) . static::substr($string, 1); } /** * Replaces the first or the last ones chars from a string by a given char. * * @param int $offset if is negative it starts from the end * @param string $replacement default is * * @return string */ public static function mask(string $string, int $offset = 0, int $length = 0, string $replacement = '*') { if ($length < 0) { throw new InvalidArgumentException('The length must equal or greater than zero.'); } $stringLength = mb_strlen($string); $absOffset = abs($offset); if ($absOffset >= $stringLength) { return $string; } $hiddenLength = $length ?: $stringLength - $absOffset; if ($offset >= 0) { return mb_substr($string, 0, $offset) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset + $hiddenLength); } return mb_substr($string, 0, max($stringLength - $hiddenLength - $absOffset, 0)) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset); } /** * Determine if a given value is a valid ULID. * * @param mixed $value */ public static function isUlid($value): bool { if (! is_string($value)) { return false; } if (strlen($value) !== 26) { return false; } if (strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz') !== 26) { return false; } return $value[0] <= '7'; } /** * Generate a ULID. */ public static function ulid(?DateTimeInterface $time = null): Ulid { if (! class_exists(Ulid::class)) { throw new RuntimeException('The "symfony/uid" package is required to use the "ulid" method. Please run "composer require symfony/uid".'); } return new Ulid(Ulid::generate($time)); } /** * Determine if a given value is a valid URL. * * @param string $value */ public static function isUrl($value, array $protocols = []): bool { if (! is_string($value)) { return false; } $protocolList = empty($protocols) ? 'aaa|aaas|about|acap|acct|acd|acr|adiumxtra|adt|afp|afs|aim|amss|android|appdata|apt|ark|attachment|aw|barion|beshare|bitcoin|bitcoincash|blob|bolo|browserext|calculator|callto|cap|cast|casts|chrome|chrome-extension|cid|coap|coap\+tcp|coap\+ws|coaps|coaps\+tcp|coaps\+ws|com-eventbrite-attendee|content|conti|crid|cvs|dab|data|dav|diaspora|dict|did|dis|dlna-playcontainer|dlna-playsingle|dns|dntp|dpp|drm|drop|dtn|dvb|ed2k|elsi|example|facetime|fax|feed|feedready|file|filesystem|finger|first-run-pen-experience|fish|fm|ftp|fuchsia-pkg|geo|gg|git|gizmoproject|go|gopher|graph|gtalk|h323|ham|hcap|hcp|http|https|hxxp|hxxps|hydrazone|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris\.beep|iris\.lwz|iris\.xpc|iris\.xpcs|isostore|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|leaptofrogans|lorawan|lvlt|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|mongodb|moz|ms-access|ms-browser-extension|ms-calculator|ms-drive-to|ms-enrollment|ms-excel|ms-eyecontrolspeech|ms-gamebarservices|ms-gamingoverlay|ms-getoffice|ms-help|ms-infopath|ms-inputapp|ms-lockscreencomponent-config|ms-media-stream-id|ms-mixedrealitycapture|ms-mobileplans|ms-officeapp|ms-people|ms-project|ms-powerpoint|ms-publisher|ms-restoretabcompanion|ms-screenclip|ms-screensketch|ms-search|ms-search-repair|ms-secondary-screen-controller|ms-secondary-screen-setup|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-connectabledevices|ms-settings-displays-topology|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|ms-spd|ms-sttoverlay|ms-transit-to|ms-useractivityset|ms-virtualtouchpad|ms-visio|ms-walk-to|ms-whiteboard|ms-whiteboard-cmd|ms-word|msnim|msrp|msrps|mss|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|ocf|oid|onenote|onenote-cmd|opaquelocktoken|openpgp4fpr|pack|palm|paparazzi|payto|pkcs11|platform|pop|pres|prospero|proxy|pwid|psyc|pttp|qb|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|s3|secondlife|service|session|sftp|sgn|shttp|sieve|simpleledger|sip|sips|skype|smb|sms|smtp|snews|snmp|soap\.beep|soap\.beeps|soldat|spiffe|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|tg|things|thismessage|tip|tn3270|tool|ts3server|turn|turns|tv|udp|unreal|urn|ut2004|v-event|vemmi|ventrilo|videotex|vnc|view-source|wais|webcal|wpid|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc\.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s' : implode('|', $protocols); /* * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (5.0.7). * * (c) Fabien Potencier http://symfony.com */ $pattern = '~^ (DEFAULT_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 \[ (?:(?:(?:(?:(?:(?:(?:[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 ) (:[0-9]+)? # a port (optional) (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})* )* # a path (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a query (optional) (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a fragment (optional) $~ixu'; return preg_match(str_replace('DEFAULT_PROTOCOLS', $protocolList, $pattern), $value) > 0; } /** * Determine if a given value is a valid UUID. * * @param mixed $value */ public static function isUuid($value): bool { if (! is_string($value)) { return false; } return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0; } /** * Generate a UUID (version 4). */ public static function uuid(): UuidInterface { if (! class_exists(Uuid::class)) { throw new RuntimeException('The "ramsey/uuid" package is required to use the "uuid" method. Please run "composer require ramsey/uuid".'); } return Uuid::uuid4(); } /** * Generate a time-ordered UUID. */ public static function orderedUuid(?DateTimeInterface $time = null): UuidInterface { if (! class_exists(Uuid::class)) { throw new RuntimeException('The "ramsey/uuid" package is required to use the "orderedUuid" method. Please run "composer require ramsey/uuid".'); } return Uuid::uuid7($time); } /** * Get the smallest possible portion of a string between two given values. * * @param string $subject * @param string $from * @param string $to * @return string */ public static function betweenFirst($subject, $from, $to) { if ($from === '' || $to === '') { return $subject; } return Str::before(Str::after($subject, $from), $to); } /** * @param string $value */ public static function classNamespace($value): string { if ($pos = strrpos($value, '\\')) { return substr($value, 0, $pos); } return ''; } /** * Convert the case of a string. */ public static function convertCase(string $string, int $mode = MB_CASE_FOLD, ?string $encoding = 'UTF-8'): string { return mb_convert_case($string, $mode, $encoding); } /** * Extracts an excerpt from text that matches the first instance of a phrase. * * @param string $text * @param string $phrase * @param array $options * @return null|string */ public static function excerpt($text, $phrase = '', $options = []) { $radius = $options['radius'] ?? 100; $omission = $options['omission'] ?? '...'; preg_match('/^(.*?)(' . preg_quote((string) $phrase) . ')(.*)$/iu', (string) $text, $matches); if (empty($matches)) { return null; } $startStr = ltrim($matches[1]); $start = Str::of(mb_substr($matches[1], max(mb_strlen($startStr, 'UTF-8') - $radius, 0), $radius, 'UTF-8'))->ltrim(); $start = $start->unless( (fn ($startWithRadius) => $startWithRadius->exactly($startStr))($start), fn ($startWithRadius) => $startWithRadius->prepend($omission), ); $endStr = rtrim($matches[3]); $end = Str::of(mb_substr($endStr, 0, $radius, 'UTF-8'))->rtrim(); $end = $end->unless( (fn ($endWithRadius) => $endWithRadius->exactly($endStr))($end), fn ($endWithRadius) => $endWithRadius->append($omission), ); return $start->append($matches[2], (string) $end)->__toString(); } /** * Determine if a given value is valid JSON. * * @param mixed $value */ public static function isJson($value): bool { if (! is_string($value)) { return false; } if (function_exists('json_validate')) { return json_validate($value, 512); } try { json_decode($value, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { return false; } return true; } /** * Make a string's first character lowercase. * * @param string $string */ public static function lcfirst($string): string { return Str::lower(Str::substr($string, 0, 1)) . Str::substr($string, 1); } /** * Generate a random, secure password. * * @param int $length * @param bool $letters * @param bool $numbers * @param bool $symbols * @param bool $spaces * @return string */ public static function password($length = 32, $letters = true, $numbers = true, $symbols = true, $spaces = false) { return (new Collection()) ->when($letters, fn ($c) => $c->merge([ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ])) ->when($numbers, fn ($c) => $c->merge([ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ])) ->when($symbols, fn ($c) => $c->merge([ '~', '!', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '.', ',', '<', '>', '?', '/', '\\', '{', '}', '[', ']', '|', ':', ';', ])) ->when($spaces, fn ($c) => $c->merge([' '])) ->pipe(fn ($c) => Collection::times($length, fn () => $c[random_int(0, $c->count() - 1)])) ->implode(''); } /** * Replace the first occurrence of the given value if it appears at the start of the string. * * @param string $search * @param string $replace * @param string $subject * @return string */ public static function replaceStart($search, $replace, $subject) { $search = (string) $search; if ($search === '') { return $subject; } if (static::startsWith($subject, $search)) { return static::replaceFirst($search, $replace, $subject); } return $subject; } /** * Replace the last occurrence of a given value if it appears at the end of the string. * * @param string $search * @param string $replace * @param string $subject * @return string */ public static function replaceEnd($search, $replace, $subject) { $search = (string) $search; if ($search === '') { return $subject; } if (static::endsWith($subject, $search)) { return static::replaceLast($search, $replace, $subject); } return $subject; } /** * Replace the patterns matching the given regular expression. * * @param string $pattern * @param Closure|string $replace * @param array|string $subject * @param int $limit * @return null|string|string[] */ public static function replaceMatches($pattern, $replace, $subject, $limit = -1) { if ($replace instanceof Closure) { return preg_replace_callback($pattern, $replace, $subject, $limit); } return preg_replace($pattern, $replace, $subject, $limit); } /** * @param string $value */ public static function reverse($value): string { return implode(array_reverse(mb_str_split($value))); } /** * Remove all whitespace from both ends of a string. * * @param string $value * @param null|string $charlist * @return string */ public static function trim($value, $charlist = null) { if ($charlist === null) { return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}]+|[\s\x{FEFF}\x{200B}\x{200E}]+$~u', '', $value) ?? trim($value); } return trim($value, $charlist); } /** * Remove all whitespace from the beginning of a string. * * @param string $value * @param null|string $charlist * @return string */ public static function ltrim($value, $charlist = null) { if ($charlist === null) { return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}]+~u', '', $value) ?? ltrim($value); } return ltrim($value, $charlist); } /** * Remove all whitespace from the end of a string. * * @param string $value * @param null|string $charlist * @return string */ public static function rtrim($value, $charlist = null) { if ($charlist === null) { return preg_replace('~[\s\x{FEFF}\x{200B}\x{200E}]+$~u', '', $value) ?? rtrim($value); } return rtrim($value, $charlist); } /** * Remove all "extra" blank space from the given string. * * @param string $value */ public static function squish($value): null|array|string { return preg_replace('~(\s|\x{3164}|\x{1160})+~u', ' ', static::trim($value)); } /** * Replace text within a portion of a string. * * @param string|string[] $string * @param string|string[] $replace * @param int|int[] $offset * @param null|int|int[] $length * @return string|string[] */ public static function substrReplace($string, $replace, $offset = 0, $length = null): array|string { if ($length === null) { $length = strlen($string); } return substr_replace($string, $replace, $offset, $length); } /** * Swap multiple keywords in a string with other keywords. * * @param string $subject * @return string */ public static function swap(array $map, $subject): array|string { return str_replace(array_keys($map), array_values($map), $subject); } /** * Take the first or last {$limit} characters of a string. */ public static function take(string $string, int $limit): string { if ($limit < 0) { return static::substr($string, $limit); } return static::substr($string, 0, $limit); } /** * Convert the given string to Base64 encoding. * * @param string $string */ public static function toBase64($string): string { return base64_encode($string); } /** * Split a string into pieces by uppercase characters. * * @param string $string * @return bool|string[] */ public static function ucsplit($string): array|bool { return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY); } /** * Unwrap the string with the given strings. * * @param string $value * @param string $before * @param null|string $after */ public static function unwrap($value, $before, $after = null): string { if (static::startsWith($value, $before)) { $value = static::substr($value, static::length($before)); } if (static::endsWith($value, $after ??= $before)) { $value = static::substr($value, 0, -static::length($after)); } return $value; } /** * Get the number of words a string contains. * * @param string $string */ public static function wordCount($string): array|int { return str_word_count($string); } /** * Wrap the string with the given strings. * * @param string $value * @param string $before * @param null|string $after */ public static function wrap($value, $before, $after = null): string { return $before . $value . ($after ??= $before); } /** * Wrap a string to a given number of characters. * * @param string $string * @param int $characters * @param string $break * @param bool $cutLongWords */ public static function wordWrap($string, $characters = 75, $break = "\n", $cutLongWords = false): string { return wordwrap($string, $characters, $break, $cutLongWords); } /** * Remove all non-numeric characters from a string. */ public static function numbers(array|string $value): array|string { return preg_replace('/[^0-9]/', '', $value); } /** * Decode the given Base64 encoded string. */ public static function fromBase64(string $string, bool $strict = false): false|string { return base64_decode($string, $strict); } /** * Returns the replacements for the ascii method. * Note: Adapted from Stringy\Stringy. * * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt */ protected static function charsArray(): array { static $charsArray; if (isset($charsArray)) { return $charsArray; } return $charsArray = [ '0' => ['°', '₀', '۰', '0'], '1' => ['¹', '₁', '۱', '1'], '2' => ['²', '₂', '۲', '2'], '3' => ['³', '₃', '۳', '3'], '4' => ['⁴', '₄', '۴', '٤', '4'], '5' => ['⁵', '₅', '۵', '٥', '5'], '6' => ['⁶', '₆', '۶', '٦', '6'], '7' => ['⁷', '₇', '۷', '7'], '8' => ['⁸', '₈', '۸', '8'], '9' => ['⁹', '₉', '۹', '9'], 'a' => [ 'à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä', ], 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], 'e' => [ 'é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e', ], 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'], 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], 'i' => [ 'í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i', ], 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'], 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'], 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'], 'o' => [ 'ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö', ], 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], 'q' => ['ყ', 'q'], 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'], 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'], 'u' => [ 'ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü', ], 'v' => ['в', 'ვ', 'ϐ', 'v'], 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], 'x' => ['χ', 'ξ', 'x'], 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], 'aa' => ['ع', 'आ', 'آ'], 'ae' => ['æ', 'ǽ'], 'ai' => ['ऐ'], 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], 'dj' => ['ђ', 'đ'], 'dz' => ['џ', 'ძ'], 'ei' => ['ऍ'], 'gh' => ['غ', 'ღ'], 'ii' => ['ई'], 'ij' => ['ij'], 'kh' => ['х', 'خ', 'ხ'], 'lj' => ['љ'], 'nj' => ['њ'], 'oe' => ['ö', 'œ', 'ؤ'], 'oi' => ['ऑ'], 'oii' => ['ऒ'], 'ps' => ['ψ'], 'sh' => ['ш', 'შ', 'ش'], 'shch' => ['щ'], 'ss' => ['ß'], 'sx' => ['ŝ'], 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], 'ts' => ['ц', 'ც', 'წ'], 'ue' => ['ü'], 'uu' => ['ऊ'], 'ya' => ['я'], 'yu' => ['ю'], 'zh' => ['ж', 'ჟ', 'ژ'], '(c)' => ['©'], 'A' => [ 'Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä', ], 'B' => ['Б', 'Β', 'ब', 'B'], 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'], 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'], 'E' => [ 'É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E', ], 'F' => ['Ф', 'Φ', 'F'], 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], 'H' => ['Η', 'Ή', 'Ħ', 'H'], 'I' => [ 'Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I', ], 'J' => ['J'], 'K' => ['К', 'Κ', 'K'], 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], 'M' => ['М', 'Μ', 'M'], 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], 'O' => [ 'Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö', ], 'P' => ['П', 'Π', 'P'], 'Q' => ['Q'], 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], 'U' => [ 'Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü', ], 'V' => ['В', 'V'], 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], 'X' => ['Χ', 'Ξ', 'X'], 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], 'AE' => ['Æ', 'Ǽ'], 'Ch' => ['Ч'], 'Dj' => ['Ђ'], 'Dz' => ['Џ'], 'Gx' => ['Ĝ'], 'Hx' => ['Ĥ'], 'Ij' => ['IJ'], 'Jx' => ['Ĵ'], 'Kh' => ['Х'], 'Lj' => ['Љ'], 'Nj' => ['Њ'], 'Oe' => ['Œ'], 'Ps' => ['Ψ'], 'Sh' => ['Ш'], 'Shch' => ['Щ'], 'Ss' => ['ẞ'], 'Th' => ['Þ'], 'Ts' => ['Ц'], 'Ya' => ['Я'], 'Yu' => ['Ю'], 'Zh' => ['Ж'], ' ' => [ "\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0", ], ]; } /** * Returns the language specific replacements for the ascii method. * Note: Adapted from Stringy\Stringy. * * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt * @return null|array */ protected static function languageSpecificCharsArray(string $language) { static $languageSpecific; if (! isset($languageSpecific)) { $languageSpecific = [ 'bg' => [ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'], ], 'de' => [ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], ], ]; } return $languageSpecific[$language] ?? null; } } src/StrCache.php000064400000002622151360551400007542 0ustar00value = (string) $value; } /** * Proxy dynamic properties onto methods. * * @param string $key * @return mixed */ public function __get($key) { return $this->{$key}(); } /** * Get the raw string value. * * @return string */ public function __toString() { return $this->value; } /** * Return the remainder of a string after the first occurrence of a given value. * * @param string $search * @return static */ public function after($search) { return new static(Str::after($this->value, $search)); } /** * Return the remainder of a string after the last occurrence of a given value. * * @param string $search * @return static */ public function afterLast($search) { return new static(Str::afterLast($this->value, $search)); } /** * Convert the given string to APA-style title case. * * @return static */ public function apa() { return new static(Str::apa($this->value)); } /** * Append the given values to the string. * * @param string $values * @return static */ public function append(...$values) { return new static($this->value . implode('', $values)); } /** * Transliterate a UTF-8 value to ASCII. * * @param string $language * @return static */ public function ascii($language = 'en') { return new static(Str::ascii($this->value, $language)); } /** * Get the trailing name component of the path. * * @param string $suffix * @return static */ public function basename($suffix = '') { return new static(basename($this->value, $suffix)); } /** * Get the character at the specified index. * * @param int $index * @return false|string */ public function charAt($index) { return Str::charAt($this->value, $index); } /** * Remove the given string if it exists at the end of the current string. * * @param array|string $needle * @return static */ public function chopEnd($needle) { return new static(Str::chopEnd($this->value, $needle)); } /** * Remove the given string if it exists at the start of the current string. * * @param array|string $needle * @return static */ public function chopStart($needle) { return new static(Str::chopStart($this->value, $needle)); } /** * Get the basename of the class path. * * @return static */ public function classBasename() { return new static(class_basename($this->value)); } /** * Get the portion of a string before the first occurrence of a given value. * * @param string $search * @return static */ public function before($search) { return new static(Str::before($this->value, $search)); } /** * Get the portion of a string before the last occurrence of a given value. * * @param string $search * @return static */ public function beforeLast($search) { return new static(Str::beforeLast($this->value, $search)); } /** * Get the portion of a string between two given values. * * @param string $from * @param string $to * @return static */ public function between($from, $to) { return new static(Str::between($this->value, $from, $to)); } /** * Convert a value to camel case. * * @return static */ public function camel() { return new static(Str::camel($this->value)); } /** * Determine if a given string contains a given substring. * * @param string|string[] $needles * @return bool */ public function contains($needles) { return Str::contains($this->value, $needles); } /** * Determine if a given string contains all array values. * * @return bool */ public function containsAll(array $needles) { return Str::containsAll($this->value, $needles); } /** * Get the parent directory's path. * * @param int $levels * @return static */ public function dirname($levels = 1) { return new static(dirname($this->value, $levels)); } /** * Determine if a given string ends with a given substring. * * @param string|string[] $needles * @return bool */ public function endsWith($needles) { return Str::endsWith($this->value, $needles); } /** * Determine if the string is an exact match with the given value. * * @param string|\Stringable $value * @return bool */ public function exactly($value) { if ($value instanceof \Stringable) { $value = $value->__toString(); } return $this->value === $value; } /** * Explode the string into an array. * * @param string $delimiter * @param int $limit * @return Collection */ public function explode($delimiter, $limit = PHP_INT_MAX) { return collect(explode($delimiter, $this->value, $limit)); } /** * Split a string using a regular expression or by length. * * @param int|string $pattern * @param int $limit * @param int $flags * @return Collection */ public function split($pattern, $limit = -1, $flags = 0) { if (filter_var($pattern, FILTER_VALIDATE_INT) !== false) { return collect(mb_str_split($this->value, $pattern)); } $segments = preg_split($pattern, $this->value, $limit, $flags); return ! empty($segments) ? collect($segments) : collect(); } /** * Cap a string with a single instance of a given value. * * @param string $cap * @return static */ public function finish($cap) { return new static(Str::finish($this->value, $cap)); } /** * Determine if a given string matches a given pattern. * * @param string|string[] $pattern * @return bool */ public function is($pattern) { return Str::is($pattern, $this->value); } /** * Determine if a given string is 7 bit ASCII. * * @return bool */ public function isAscii() { return Str::isAscii($this->value); } /** * Determine if the given string is empty. * * @return bool */ public function isEmpty() { return $this->value === ''; } /** * Determine if the given string is not empty. * * @return bool */ public function isNotEmpty() { return ! $this->isEmpty(); } public function isUlid(): bool { return Str::isUlid($this->value); } public function isUrl(): bool { return Str::isUrl($this->value); } public function isUuid(): bool { return Str::isUuid($this->value); } /** * Convert a string to kebab case. * * @return static */ public function kebab() { return new static(Str::kebab($this->value)); } /** * Return the length of the given string. * * @param string $encoding * @return int */ public function length($encoding = null) { return Str::length($this->value, $encoding); } /** * Limit the number of characters in a string. * * @param int $limit * @param string $end * @return static */ public function limit($limit = 100, $end = '...') { return new static(Str::limit($this->value, $limit, $end)); } /** * Convert the given string to lower-case. * * @return static */ public function lower() { return new static(Str::lower($this->value)); } /** * Get the string matching the given pattern. * * @param string $pattern * @return static */ public function match($pattern) { preg_match($pattern, $this->value, $matches); if (! $matches) { return new static(); } return new static($matches[1] ?? $matches[0]); } /** * Determine if a given string matches a given pattern. * * @param iterable|string $pattern * @return bool */ public function isMatch($pattern) { return Str::isMatch($pattern, $this->value); } /** * Get the string matching the given pattern. * * @param string $pattern * @return Collection */ public function matchAll($pattern) { preg_match_all($pattern, $this->value, $matches); if (empty($matches[0])) { return collect(); } return collect($matches[1] ?? $matches[0]); } /** * Determine if the string matches the given pattern. * * @param string $pattern * @return bool */ public function test($pattern) { return $this->match($pattern)->isNotEmpty(); } /** * Pad both sides of the string with another. * * @param int $length * @param string $pad * @return static */ public function padBoth($length, $pad = ' ') { return new static(Str::padBoth($this->value, $length, $pad)); } /** * Pad the left side of the string with another. * * @param int $length * @param string $pad * @return static */ public function padLeft($length, $pad = ' ') { return new static(Str::padLeft($this->value, $length, $pad)); } /** * Pad the right side of the string with another. * * @param int $length * @param string $pad * @return static */ public function padRight($length, $pad = ' ') { return new static(Str::padRight($this->value, $length, $pad)); } /** * Parse a Class@method style callback into class and method. * * @param null|string $default * @return array */ public function parseCallback($default = null) { return Str::parseCallback($this->value, $default); } /** * Call the given callback and return a new string. * * @return static */ public function pipe(callable $callback) { return new static(call_user_func($callback, $this)); } /** * Get the plural form of an English word. * * @param int $count * @return static */ public function plural($count = 2) { return new static(Str::plural($this->value, $count)); } /** * Pluralize the last word of an English, studly caps case string. * * @param int $count * @return static */ public function pluralStudly($count = 2) { return new static(Str::pluralStudly($this->value, $count)); } /** * Find the multi-byte safe position of the first occurrence of the given substring. * * @param string $needle * @param int $offset * @param null|string $encoding * @return false|int */ public function position($needle, $offset = 0, $encoding = null) { return Str::position($this->value, $needle, $offset, $encoding); } /** * Prepend the given values to the string. * * @param string $values * @return static */ public function prepend(...$values) { return new static(implode('', $values) . $this->value); } /** * Remove any occurrence of the given string in the subject. * * @param string|string[] $search * @param bool $caseSensitive * @return static */ public function remove($search, $caseSensitive = true) { return new static(Str::remove($search, $this->value, $caseSensitive)); } /** * Repeat the string. * * @return static */ public function repeat(int $times) { return new static(Str::repeat($this->value, $times)); } /** * Replace the given value in the given string. * * @param string|string[] $search * @param string|string[] $replace * @return static */ public function replace($search, $replace) { return new static(Str::replace($search, $replace, $this->value)); } /** * Replace a given value in the string sequentially with an array. * * @param string $search * @return static */ public function replaceArray($search, array $replace) { return new static(Str::replaceArray($search, $replace, $this->value)); } /** * Replace the first occurrence of a given value in the string. * * @param string $search * @param string $replace * @return static */ public function replaceFirst($search, $replace) { return new static(Str::replaceFirst($search, $replace, $this->value)); } /** * Replace the last occurrence of a given value in the string. * * @param string $search * @param string $replace * @return static */ public function replaceLast($search, $replace) { return new static(Str::replaceLast($search, $replace, $this->value)); } /** * Replace the patterns matching the given regular expression. * * @param string $pattern * @param Closure|string $replace * @param int $limit * @return static */ public function replaceMatches($pattern, $replace, $limit = -1) { return new static(Str::replaceMatches($pattern, $replace, $this->value, $limit)); } /** * Begin a string with a single instance of a given value. * * @param string $prefix * @return static */ public function start($prefix) { return new static(Str::start($this->value, $prefix)); } /** * Strip HTML and PHP tags from the given string. * * @param null|string|string[] $allowedTags * @return static */ public function stripTags($allowedTags = null) { return new static(strip_tags($this->value, $allowedTags)); } /** * Convert the given string to upper-case. * * @return static */ public function upper() { return new static(Str::upper($this->value)); } /** * Convert the given string to title case. * * @return static */ public function title() { return new static(Str::title($this->value)); } /** * Convert the given string to proper case for each word. * * @return static */ public function headline() { return new static(Str::headline($this->value)); } /** * Get the singular form of an English word. * * @return static */ public function singular() { return new static(Str::singular($this->value)); } /** * Generate a URL friendly "slug" from a given string. * * @param string $separator * @param null|string $language * @return static */ public function slug($separator = '-', $language = 'en') { return new static(Str::slug($this->value, $separator, $language)); } /** * Convert a string to snake case. * * @param string $delimiter * @return static */ public function snake($delimiter = '_') { return new static(Str::snake($this->value, $delimiter)); } /** * Determine if a given string starts with a given substring. * * @param string|string[] $needles * @return bool */ public function startsWith($needles) { return Str::startsWith($this->value, $needles); } /** * Convert a value to studly caps case. * * @return static */ public function studly() { return new static(Str::studly($this->value)); } /** * Returns the portion of the string specified by the start and length parameters. * * @param int $start * @param null|int $length * @return static */ public function substr($start, $length = null) { return new static(Str::substr($this->value, $start, $length)); } /** * Returns the number of substring occurrences. * * @param string $needle * @param null|int $offset * @param null|int $length * @return int */ public function substrCount($needle, $offset = null, $length = null) { return Str::substrCount($this->value, $needle, $offset ?? 0, $length); } /** * Take the first or last {$limit} characters. * * @return static */ public function take(int $limit) { if ($limit < 0) { return $this->substr($limit); } return $this->substr(0, $limit); } /** * Convert the string to Base64 encoding. * * @return static */ public function toBase64() { return new static(base64_encode($this->value)); } /** * Trim the string of the given characters. * * @param string $characters * @return static */ public function trim($characters = null) { return new static(Str::trim(...array_merge([$this->value], func_get_args()))); } /** * Left trim the string of the given characters. * * @param string $characters * @return static */ public function ltrim($characters = null) { return new static(Str::ltrim(...array_merge([$this->value], func_get_args()))); } /** * Right trim the string of the given characters. * * @param string $characters * @return static */ public function rtrim($characters = null) { return new static(Str::rtrim(...array_merge([$this->value], func_get_args()))); } /** * Make a string's first character uppercase. * * @return static */ public function ucfirst() { return new static(Str::ucfirst($this->value)); } /** * Unwrap the string with the given strings. * * @param string $before * @param null|string $after * @return static */ public function unwrap($before, $after = null) { return new static(Str::unwrap($this->value, $before, $after)); } /** * Replaces the first or the last ones chars from a string by a given char. * * @param int $offset if is negative it starts from the end * @param string $replacement default is * * @return static */ public function mask(int $offset = 0, int $length = 0, string $replacement = '*') { return new static(Str::mask($this->value, $offset, $length, $replacement)); } /** * Execute the given callback if the string is empty. * * @param callable $callback * @param null|callable $default * @return static */ public function whenEmpty($callback, $default = null) { return $this->when($this->isEmpty(), $callback, $default); } /** * Execute the given callback if the string is not empty. * * @param callable $callback * @param null|callable $default * @return static */ public function whenNotEmpty($callback, $default = null) { return $this->when($this->isNotEmpty(), $callback, $default); } /** * Limit the number of words in a string. * * @param int $words * @param string $end * @return static */ public function words($words = 100, $end = '...') { return new static(Str::words($this->value, $words, $end)); } /** * Get the number of words a string contains. * * @return int */ public function wordCount() { return str_word_count($this->value); } /** * Convert the object to a string when JSON encoded. */ public function jsonSerialize(): mixed { return $this->__toString(); } /** * Determine if the given offset exists. */ public function offsetExists(mixed $offset): bool { return isset($this->value[$offset]); } /** * Get the value at the given offset. */ public function offsetGet(mixed $offset): string { return $this->value[$offset]; } /** * Set the value at the given offset. */ public function offsetSet(mixed $offset, mixed $value): void { $this->value[$offset] = $value; } /** * Unset the value at the given offset. */ public function offsetUnset(mixed $offset): void { unset($this->value[$offset]); } public function betweenFirst($from, $to): static { return new static(Str::betweenFirst($this->value, $from, $to)); } public function classNamespace(): static { return new static(Str::classNamespace($this->value)); } public function convertCase($mode = MB_CASE_FOLD, $encoding = 'UTF-8'): static { return new static(Str::convertCase($this->value, $mode, $encoding)); } public function excerpt($phrase = '', $options = []): ?string { return Str::excerpt($this->value, $phrase, $options); } public function isJson(): bool { return Str::isJson($this->value); } public function lcfirst(): static { return new static(Str::lcfirst($this->value)); } public function newLine($count = 1): static { return $this->append(str_repeat(PHP_EOL, $count)); } public function replaceStart($search, $replace): static { return new static(Str::replaceStart($search, $replace, $this->value)); } public function replaceEnd($search, $replace): static { return new static(Str::replaceEnd($search, $replace, $this->value)); } public function reverse(): static { return new static(Str::reverse($this->value)); } public function scan($format): Collection { return collect(sscanf($this->value, $format)); } public function squish(): static { return new static(Str::squish($this->value)); } public function substrReplace($replace, $offset = 0, $length = null): static { return new static(Str::substrReplace($this->value, $replace, $offset, $length)); } public function swap(array $map) { return new static(strtr($this->value, $map)); } public function toString(): string { return $this->value; } public function ucsplit(): Collection { return collect(Str::ucsplit($this->value)); } public function value(): string { return $this->toString(); } public function whenContains($needles, $callback, $default = null) { return $this->when($this->contains($needles), $callback, $default); } public function whenContainsAll(array $needles, $callback, $default = null) { return $this->when($this->containsAll($needles), $callback, $default); } public function whenEndsWith($needles, $callback, $default = null) { return $this->when($this->endsWith($needles), $callback, $default); } public function whenExactly($needles, $callback, $default = null) { return $this->when($this->exactly($needles), $callback, $default); } public function whenIs($pattern, $callback, $default = null) { return $this->when($this->is($pattern), $callback, $default); } public function whenIsUlid($callback, $default = null) { return $this->when($this->isUlid(), $callback, $default); } public function whenIsUuid($callback, $default = null) { return $this->when($this->isUuid(), $callback, $default); } public function whenNotExactly($needles, $callback, $default = null) { return $this->when(! $this->exactly($needles), $callback, $default); } public function whenStartsWith($needles, $callback, $default = null) { return $this->when($this->startsWith($needles), $callback, $default); } public function whenTest($pattern, $callback, $default = null) { return $this->when($this->test($pattern), $callback, $default); } public function wrap($before, $after = null) { return new static(Str::wrap($this->value, $before, $after)); } /** * Wrap a string to a given number of characters. * * @param mixed $characters * @param mixed $break * @param mixed $cutLongWords */ public function wordWrap($characters = 75, $break = "\n", $cutLongWords = false): static { return new static(Str::wordWrap($this->value, $characters, $break, $cutLongWords)); } }