Revision 621 | Blame | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Illuminate\Support;use Closure;use Illuminate\Support\Traits\Macroable;use JsonException;use League\CommonMark\Environment\Environment;use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;use League\CommonMark\Extension\InlinesOnly\InlinesOnlyExtension;use League\CommonMark\GithubFlavoredMarkdownConverter;use League\CommonMark\MarkdownConverter;use Ramsey\Uuid\Codec\TimestampFirstCombCodec;use Ramsey\Uuid\Generator\CombGenerator;use Ramsey\Uuid\Uuid;use Ramsey\Uuid\UuidFactory;use Symfony\Component\Uid\Ulid;use Traversable;use voku\helper\ASCII;class Str{use Macroable;/*** The cache of snake-cased words.** @var array*/protected static $snakeCache = [];/*** The cache of camel-cased words.** @var array*/protected static $camelCache = [];/*** The cache of studly-cased words.** @var array*/protected static $studlyCache = [];/*** The callback that should be used to generate UUIDs.** @var callable|null*/protected static $uuidFactory;/*** The callback that should be used to generate random strings.** @var callable|null*/protected static $randomStringFactory;/*** Get a new stringable object from the given string.** @param string $string* @return \Illuminate\Support\Stringable*/public static function of($string){return new Stringable($string);}/*** Return the remainder of a string after the first occurrence of a given value.** @param string $subject* @param string $search* @return string*/public static function after($subject, $search){return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];}/*** Return the remainder of a string after the last occurrence of a given value.** @param string $subject* @param string $search* @return string*/public static function afterLast($subject, $search){if ($search === '') {return $subject;}$position = strrpos($subject, (string) $search);if ($position === false) {return $subject;}return substr($subject, $position + strlen($search));}/*** Transliterate a UTF-8 value to ASCII.** @param string $value* @param string $language* @return string*/public static function ascii($value, $language = 'en'){return ASCII::to_ascii((string) $value, $language);}/*** Transliterate a string to its closest ASCII representation.** @param string $string* @param string|null $unknown* @param bool|null $strict* @return string*/public static function transliterate($string, $unknown = '?', $strict = false){return ASCII::to_transliterate($string, $unknown, $strict);}/*** Get the portion of a string before the first occurrence of a given value.** @param string $subject* @param string $search* @return string*/public static function before($subject, $search){if ($search === '') {return $subject;}$result = strstr($subject, (string) $search, true);return $result === false ? $subject : $result;}/*** 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);}/*** 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 static::before(static::after($subject, $from), $to);}/*** Convert a value to camel case.** @param string $value* @return string*/public static function camel($value){if (isset(static::$camelCache[$value])) {return static::$camelCache[$value];}return static::$camelCache[$value] = lcfirst(static::studly($value));}/*** Determine if a given string contains a given substring.** @param string $haystack* @param string|iterable<string> $needles* @param bool $ignoreCase* @return bool*/public static function contains($haystack, $needles, $ignoreCase = false){if ($ignoreCase) {$haystack = mb_strtolower($haystack);}if (! is_iterable($needles)) {$needles = (array) $needles;}foreach ($needles as $needle) {if ($ignoreCase) {$needle = mb_strtolower($needle);}if ($needle !== '' && str_contains($haystack, $needle)) {return true;}}return false;}/*** Determine if a given string contains all array values.** @param string $haystack* @param iterable<string> $needles* @param bool $ignoreCase* @return bool*/public static function containsAll($haystack, $needles, $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 string $haystack* @param string|iterable<string> $needles* @return bool*/public static function endsWith($haystack, $needles){if (! is_iterable($needles)) {$needles = (array) $needles;}foreach ($needles as $needle) {if ((string) $needle !== '' && str_ends_with($haystack, $needle)) {return true;}}return false;}/*** Extracts an excerpt from text that matches the first instance of a phrase.** @param string $text* @param string $phrase* @param array $options* @return string|null*/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;}$start = ltrim($matches[1]);$start = str(mb_substr($start, max(mb_strlen($start, 'UTF-8') - $radius, 0), $radius, 'UTF-8'))->ltrim()->unless(fn ($startWithRadius) => $startWithRadius->exactly($start),fn ($startWithRadius) => $startWithRadius->prepend($omission),);$end = rtrim($matches[3]);$end = str(mb_substr($end, 0, $radius, 'UTF-8'))->rtrim()->unless(fn ($endWithRadius) => $endWithRadius->exactly($end),fn ($endWithRadius) => $endWithRadius->append($omission),);return $start->append($matches[2], $end)->toString();}/*** 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;}/*** Wrap the string with the given strings.** @param string $value* @param string $before* @param string|null $after* @return string*/public static function wrap($value, $before, $after = null){return $before.$value.($after ??= $before);}/*** Determine if a given string matches a given pattern.** @param string|iterable<string> $pattern* @param string $value* @return bool*/public static function is($pattern, $value){$value = (string) $value;if (! is_iterable($pattern)) {$pattern = [$pattern];}foreach ($pattern 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.** @param string $value* @return bool*/public static function isAscii($value){return ASCII::is_ascii((string) $value);}/*** Determine if a given string is valid JSON.** @param string $value* @return bool*/public static function isJson($value){if (! is_string($value)) {return false;}try {json_decode($value, true, 512, JSON_THROW_ON_ERROR);} catch (JsonException) {return false;}return true;}/*** Determine if a given string is a valid UUID.** @param string $value* @return bool*/public static function isUuid($value){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;}/*** Determine if a given string is a valid ULID.** @param string $value* @return bool*/public static function isUlid($value){if (! is_string($value)) {return false;}return Ulid::isValid($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|null $encoding* @return int*/public static function length($value, $encoding = null){if ($encoding) {return mb_strlen($value, $encoding);}return mb_strlen($value);}/*** 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.** @param string $value* @param int $words* @param string $end* @return string*/public static function words($value, $words = 100, $end = '...'){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;}/*** Converts GitHub flavored Markdown into HTML.** @param string $string* @param array $options* @return string*/public static function markdown($string, array $options = []){$converter = new GithubFlavoredMarkdownConverter($options);return (string) $converter->convert($string);}/*** Converts inline Markdown into HTML.** @param string $string* @param array $options* @return string*/public static function inlineMarkdown($string, array $options = []){$environment = new Environment($options);$environment->addExtension(new GithubFlavoredMarkdownExtension());$environment->addExtension(new InlinesOnlyExtension());$converter = new MarkdownConverter($environment);return (string) $converter->convert($string);}/*** Masks a portion of a string with a repeated character.** @param string $string* @param string $character* @param int $index* @param int|null $length* @param string $encoding* @return string*/public static function mask($string, $character, $index, $length = null, $encoding = 'UTF-8'){if ($character === '') {return $string;}$segment = mb_substr($string, $index, $length, $encoding);if ($segment === '') {return $string;}$strlen = mb_strlen($string, $encoding);$startIndex = $index;if ($index < 0) {$startIndex = $index < -$strlen ? 0 : $strlen + $index;}$start = mb_substr($string, 0, $startIndex, $encoding);$segmentLen = mb_strlen($segment, $encoding);$end = mb_substr($string, $startIndex + $segmentLen);return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$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];}/*** Get the string matching the given pattern.** @param string $pattern* @param string $subject* @return \Illuminate\Support\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 = ' '){$short = max(0, $length - mb_strlen($value));$shortLeft = floor($short / 2);$shortRight = ceil($short / 2);return mb_substr(str_repeat($pad, $shortLeft), 0, $shortLeft).$value.mb_substr(str_repeat($pad, $shortRight), 0, $shortRight);}/*** 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 = ' '){$short = max(0, $length - mb_strlen($value));return mb_substr(str_repeat($pad, $short), 0, $short).$value;}/*** 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 = ' '){$short = max(0, $length - mb_strlen($value));return $value.mb_substr(str_repeat($pad, $short), 0, $short);}/*** Parse a Class[@]method style callback into class and method.** @param string $callback* @param string|null $default* @return array<int, string|null>*/public static function parseCallback($callback, $default = null){return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];}/*** Get the plural form of an English word.** @param string $value* @param int|array|\Countable $count* @return string*/public static function plural($value, $count = 2){return Pluralizer::plural($value, $count);}/*** Pluralize the last word of an English, studly caps case string.** @param string $value* @param int|array|\Countable $count* @return string*/public static function pluralStudly($value, $count = 2){$parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);$lastWord = array_pop($parts);return implode('', $parts).self::plural($lastWord, $count);}/*** Generate a more truly "random" alpha-numeric string.** @param int $length* @return string*/public static function random($length = 16){return (static::$randomStringFactory ?? function ($length) {$string = '';while (($len = strlen($string)) < $length) {$size = $length - $len;$bytesSize = (int) ceil($size / 3) * 3;$bytes = random_bytes($bytesSize);$string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);}return $string;})($length);}/*** Set the callable that will be used to generate random strings.** @param callable|null $factory* @return void*/public static function createRandomStringsUsing(callable $factory = null){static::$randomStringFactory = $factory;}/*** Set the sequence that will be used to generate random strings.** @param array $sequence* @param callable|null $whenMissing* @return void*/public static function createRandomStringsUsingSequence(array $sequence, $whenMissing = null){$next = 0;$whenMissing ??= function ($length) use (&$next) {$factoryCache = static::$randomStringFactory;static::$randomStringFactory = null;$randomString = static::random($length);static::$randomStringFactory = $factoryCache;$next++;return $randomString;};static::createRandomStringsUsing(function ($length) use (&$next, $sequence, $whenMissing) {if (array_key_exists($next, $sequence)) {return $sequence[$next++];}return $whenMissing($length);});}/*** Indicate that random strings should be created normally and not using a custom factory.** @return void*/public static function createRandomStringsNormally(){static::$randomStringFactory = null;}/*** Repeat the given string.** @param string $string* @param int $times* @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 $search* @param iterable<string> $replace* @param string $subject* @return string*/public static function replaceArray($search, $replace, $subject){if ($replace instanceof Traversable) {$replace = collect($replace)->all();}$segments = explode($search, $subject);$result = array_shift($segments);foreach ($segments as $segment) {$result .= (array_shift($replace) ?? $search).$segment;}return $result;}/*** Replace the given value in the given string.** @param string|iterable<string> $search* @param string|iterable<string> $replace* @param string|iterable<string> $subject* @return string*/public static function replace($search, $replace, $subject){if ($search instanceof Traversable) {$search = collect($search)->all();}if ($replace instanceof Traversable) {$replace = collect($replace)->all();}if ($subject instanceof Traversable) {$subject = collect($subject)->all();}return str_replace($search, $replace, $subject);}/*** Replace the first occurrence of a given value in the string.** @param string $search* @param string $replace* @param string $subject* @return string*/public static function replaceFirst($search, $replace, $subject){$search = (string) $search;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.** @param string $search* @param string $replace* @param string $subject* @return string*/public static function replaceLast($search, $replace, $subject){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 string|iterable<string> $search* @param string $subject* @param bool $caseSensitive* @return string*/public static function remove($search, $subject, $caseSensitive = true){if ($search instanceof Traversable) {$search = collect($search)->all();}$subject = $caseSensitive? str_replace($search, '', $subject): str_ireplace($search, '', $subject);return $subject;}/*** Reverse the given string.** @param string $value* @return string*/public static function reverse(string $value){return implode(array_reverse(mb_str_split($value)));}/*** Begin a string with a single instance of a given value.** @param string $value* @param string $prefix* @return string*/public static function start($value, $prefix){$quoted = preg_quote($prefix, '/');return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);}/*** Convert the given string to upper-case.** @param string $value* @return string*/public static function upper($value){return mb_strtoupper($value, 'UTF-8');}/*** Convert the given string to title case.** @param string $value* @return string*/public static function title($value){return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');}/*** Convert the given string to title case for each word.** @param string $value* @return string*/public static function headline($value){$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.** @param string $value* @return string*/public static function singular($value){return Pluralizer::singular($value);}/*** Generate a URL friendly "slug" from a given string.** @param string $title* @param string $separator* @param string|null $language* @param array<string, string> $dictionary* @return string*/public static function slug($title, $separator = '-', $language = 'en', $dictionary = ['@' => 'at']){$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 wordsforeach ($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', '', static::lower($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.** @param string $value* @param string $delimiter* @return string*/public static function snake($value, $delimiter = '_'){$key = $value;if (isset(static::$snakeCache[$key][$delimiter])) {return static::$snakeCache[$key][$delimiter];}if (! ctype_lower($value)) {$value = preg_replace('/\s+/u', '', ucwords($value));$value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));}return static::$snakeCache[$key][$delimiter] = $value;}/*** Remove all "extra" blank space from the given string.** @param string $value* @return string*/public static function squish($value){return preg_replace('~(\s|\x{3164})+~u', ' ', preg_replace('~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '', $value));}/*** Determine if a given string starts with a given substring.** @param string $haystack* @param string|iterable<string> $needles* @return bool*/public static function startsWith($haystack, $needles){if (! is_iterable($needles)) {$needles = [$needles];}foreach ($needles as $needle) {if ((string) $needle !== '' && str_starts_with($haystack, $needle)) {return true;}}return false;}/*** Convert a value to studly caps case.** @param string $value* @return string*/public static function studly($value){$key = $value;if (isset(static::$studlyCache[$key])) {return static::$studlyCache[$key];}$words = explode(' ', static::replace(['-', '_'], ' ', $value));$studlyWords = array_map(fn ($word) => static::ucfirst($word), $words);return static::$studlyCache[$key] = implode($studlyWords);}/*** Returns the portion of the string specified by the start and length parameters.** @param string $string* @param int $start* @param int|null $length* @param string $encoding* @return string*/public static function substr($string, $start, $length = null, $encoding = 'UTF-8'){return mb_substr($string, $start, $length, $encoding);}/*** Returns the number of substring occurrences.** @param string $haystack* @param string $needle* @param int $offset* @param int|null $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);}/*** Replace text within a portion of a string.** @param string|string[] $string* @param string|string[] $replace* @param int|int[] $offset* @param int|int[]|null $length* @return string|string[]*/public static function substrReplace($string, $replace, $offset = 0, $length = null){if ($length === null) {$length = strlen($string);}return substr_replace($string, $replace, $offset, $length);}/*** Swap multiple keywords in a string with other keywords.** @param array $map* @param string $subject* @return string*/public static function swap(array $map, $subject){return strtr($subject, $map);}/*** Make a string's first character lowercase.** @param string $string* @return string*/public static function lcfirst($string){return static::lower(static::substr($string, 0, 1)).static::substr($string, 1);}/*** Make a string's first character uppercase.** @param string $string* @return string*/public static function ucfirst($string){return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);}/*** Split a string into pieces by uppercase characters.** @param string $string* @return string[]*/public static function ucsplit($string){return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY);}/*** Get the number of words a string contains.** @param string $string* @param string|null $characters* @return int*/public static function wordCount($string, $characters = null){return str_word_count($string, 0, $characters);}/*** Generate a UUID (version 4).** @return \Ramsey\Uuid\UuidInterface*/public static function uuid(){return static::$uuidFactory? call_user_func(static::$uuidFactory): Uuid::uuid4();}/*** Generate a time-ordered UUID (version 4).** @return \Ramsey\Uuid\UuidInterface*/public static function orderedUuid(){if (static::$uuidFactory) {return call_user_func(static::$uuidFactory);}$factory = new UuidFactory;$factory->setRandomGenerator(new CombGenerator($factory->getRandomGenerator(),$factory->getNumberConverter()));$factory->setCodec(new TimestampFirstCombCodec($factory->getUuidBuilder()));return $factory->uuid4();}/*** Set the callable that will be used to generate UUIDs.** @param callable|null $factory* @return void*/public static function createUuidsUsing(callable $factory = null){static::$uuidFactory = $factory;}/*** Set the sequence that will be used to generate UUIDs.** @param array $sequence* @param callable|null $whenMissing* @return void*/public static function createUuidsUsingSequence(array $sequence, $whenMissing = null){$next = 0;$whenMissing ??= function () use (&$next) {$factoryCache = static::$uuidFactory;static::$uuidFactory = null;$uuid = static::uuid();static::$uuidFactory = $factoryCache;$next++;return $uuid;};static::createUuidsUsing(function () use (&$next, $sequence, $whenMissing) {if (array_key_exists($next, $sequence)) {return $sequence[$next++];}return $whenMissing();});}/*** Always return the same UUID when generating new UUIDs.** @param \Closure|null $callback* @return \Ramsey\Uuid\UuidInterface*/public static function freezeUuids(Closure $callback = null){$uuid = Str::uuid();Str::createUuidsUsing(fn () => $uuid);if ($callback !== null) {try {$callback($uuid);} finally {Str::createUuidsNormally();}}return $uuid;}/*** Indicate that UUIDs should be created normally and not using a custom factory.** @return void*/public static function createUuidsNormally(){static::$uuidFactory = null;}/*** Generate a ULID.** @return \Symfony\Component\Uid\Ulid*/public static function ulid(){return new Ulid();}/*** Remove all strings from the casing caches.** @return void*/public static function flushCache(){static::$snakeCache = [];static::$camelCache = [];static::$studlyCache = [];}}