$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 $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 $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 $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 */ 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; $bytes = random_bytes($size); $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 $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 $search * @param string|iterable $replace * @param string|iterable $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 $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 $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 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', '', 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]+|[\s]+$~u', '', $value)); } /** * Determine if a given string starts with a given substring. * * @param string $haystack * @param string|iterable $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 = []; } }