PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.22
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.22
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / vendor / wpfluent / framework / src / WPFluent / Support / Str.php

Str.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.22, at vendor/wpfluent/framework/src/WPFluent/Support/Str.php

1,394 lines 45.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\Framework\Support;
4
5 use FluentBooking\Framework\Support\Helper;
6 use FluentBooking\Framework\Support\Stringable;
7 use FluentBooking\Framework\Support\MacroableTrait;
8
9 class Str
10 {
11 use MacroableTrait;
12
13 /**
14 * The cache of snake-cased words.
15 *
16 * @var array
17 */
18 protected static $snakeCache = [];
19
20 /**
21 * The cache of camel-cased words.
22 *
23 * @var array
24 */
25 protected static $camelCache = [];
26
27 /**
28 * The cache of studly-cased words.
29 *
30 * @var array
31 */
32 protected static $studlyCache = [];
33
34 /**
35 * Get a new stringable object from the given string.
36 *
37 * @param string $string
38 * @return \FluentBooking\Framework\Support\Stringable
39 */
40 public static function of($string)
41 {
42 return new Stringable($string);
43 }
44
45 /**
46 * Makes an acronum from a string of words
47 *
48 * @param string $string
49 * @param string $delimiter
50 * @return string
51 */
52 public static function acronym($string, $delimiter = '')
53 {
54 if (empty($string)) {
55 return '';
56 }
57
58 $acronym = '';
59 foreach (preg_split('/[^\p{L}]+/u', $string) as $word) {
60 if(!empty($word)){
61 $first_letter = mb_substr($word, 0, 1);
62 $acronym .= $first_letter . $delimiter;
63 }
64 }
65
66 return $acronym;
67 }
68
69 /**
70 * Return the remainder of a string after the first occurrence of a given value.
71 *
72 * @param string $subject
73 * @param string $search
74 * @return string
75 */
76 public static function after($subject, $search)
77 {
78 return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
79 }
80
81 /**
82 * Return the remainder of a string after the last occurrence of a given value.
83 *
84 * @param string $subject
85 * @param string $search
86 * @return string
87 */
88 public static function afterLast($subject, $search)
89 {
90 if ($search === '') {
91 return $subject;
92 }
93
94 $position = strrpos($subject, (string) $search);
95
96 if ($position === false) {
97 return $subject;
98 }
99
100 return substr($subject, $position + strlen($search));
101 }
102
103 /**
104 * Transliterate a UTF-8 value to ASCII.
105 *
106 * @param string $value
107 * @return string
108 * @see https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/Internet.php#L245
109 */
110 public static function ascii($value)
111 {
112 static $arrayFrom, $arrayTo;
113
114 if (empty($arrayFrom)) {
115 $transliterationTable = [
116 'IJ' => 'I', 'Ö' => 'O', 'Œ' => 'O', 'Ü' => 'U', 'ä' => 'a', 'æ' => 'a',
117 'ij' => 'i', 'ö' => 'o', 'œ' => 'o', 'ü' => 'u', 'ß' => 's', 'ſ' => 's',
118 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', '�
119 ' => 'A',
120 'Æ' => 'A', 'Ā' => 'A', 'Ą' => 'A', 'Ă' => 'A', 'Ç' => 'C', 'Ć' => 'C',
121 'Č' => 'C', 'Ĉ' => 'C', 'Ċ' => 'C', 'Ď' => 'D', 'Đ' => 'D', 'È' => 'E',
122 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ē' => 'E', 'Ę' => 'E', 'Ě' => 'E',
123 'Ĕ' => 'E', 'Ė' => 'E', 'Ĝ' => 'G', 'Ğ' => 'G', 'Ġ' => 'G', 'Ģ' => 'G',
124 'Ĥ' => 'H', 'Ħ' => 'H', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
125 'Ī' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Į' => 'I', 'İ' => 'I', 'Ĵ' => 'J',
126 'Ķ' => 'K', 'Ľ' => 'K', 'Ĺ' => 'K', 'Ļ' => 'K', 'Ŀ' => 'K', 'Ł' => 'L',
127 'Ñ' => 'N', 'Ń' => 'N', 'Ň' => 'N', '�
128 ' => 'N', 'Ŋ' => 'N', 'Ò' => 'O',
129 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Ō' => 'O', 'Ő' => 'O',
130 'Ŏ' => 'O', 'Ŕ' => 'R', 'Ř' => 'R', 'Ŗ' => 'R', 'Ś' => 'S', 'Ş' => 'S',
131 'Ŝ' => 'S', 'Ș' => 'S', 'Š' => 'S', 'Ť' => 'T', 'Ţ' => 'T', 'Ŧ' => 'T',
132 'Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ū' => 'U', 'Ů' => 'U',
133 'Ű' => 'U', 'Ŭ' => 'U', 'Ũ' => 'U', 'Ų' => 'U', 'Ŵ' => 'W', 'Ŷ' => 'Y',
134 'Ÿ' => 'Y', 'Ý' => 'Y', 'Ź' => 'Z', 'Ż' => 'Z', 'Ž' => 'Z', 'à' => 'a',
135 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ā' => 'a', '�
136 ' => 'a', 'ă' => 'a',
137 'å' => 'a', 'ç' => 'c', 'ć' => 'c', 'č' => 'c', 'ĉ' => 'c', 'ċ' => 'c',
138 'ď' => 'd', 'đ' => 'd', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
139 'ē' => 'e', 'ę' => 'e', 'ě' => 'e', 'ĕ' => 'e', 'ė' => 'e', 'ƒ' => 'f',
140 'ĝ' => 'g', 'ğ' => 'g', 'ġ' => 'g', 'ģ' => 'g', 'ĥ' => 'h', 'ħ' => 'h',
141 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ī' => 'i', 'ĩ' => 'i',
142 'ĭ' => 'i', 'į' => 'i', 'ı' => 'i', 'ĵ' => 'j', 'ķ' => 'k', 'ĸ' => 'k',
143 'ł' => 'l', 'ľ' => 'l', 'ĺ' => 'l', 'ļ' => 'l', 'ŀ' => 'l', 'ñ' => 'n',
144 'ń' => 'n', 'ň' => 'n', 'ņ' => 'n', 'ʼn' => 'n', 'ŋ' => 'n', 'ò' => 'o',
145 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ø' => 'o', 'ō' => 'o', 'ő' => 'o',
146 'ŏ' => 'o', 'ŕ' => 'r', 'ř' => 'r', 'ŗ' => 'r', 'ś' => 's', 'š' => 's',
147 'ť' => 't', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ū' => 'u', 'ů' => 'u',
148 'ű' => 'u', 'ŭ' => 'u', 'ũ' => 'u', 'ų' => 'u', 'ŵ' => 'w', 'ÿ' => 'y',
149 'ý' => 'y', 'ŷ' => 'y', 'ż' => 'z', 'ź' => 'z', 'ž' => 'z', 'Α' => 'A',
150 'Ά' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A',
151 '' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A',
152 '' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A', '' => 'A',
153 '' => 'A', '' => 'A', '' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D',
154 'Ε' => 'E', 'Έ' => 'E', '' => 'E', '' => 'E', '' => 'E', '' => 'E',
155 '' => 'E', '' => 'E', '' => 'E', 'Ζ' => 'Z', 'Η' => 'I', 'Ή' => 'I',
156 '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I',
157 '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I',
158 '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I',
159 'Θ' => 'T', 'Ι' => 'I', 'Ί' => 'I', 'Ϊ' => 'I', '' => 'I', '' => 'I',
160 '' => 'I', '' => 'I', '' => 'I', '' => 'I', '' => 'I', 'Ἷ' => 'I',
161 '' => 'I', '' => 'I', '' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M',
162 'Ν' => 'N', 'Ξ' => 'K', 'Ο' => 'O', 'Ό' => 'O', '' => 'O', '' => 'O',
163 '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O', 'Π' => 'P',
164 'Ρ' => 'R', '' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Ύ' => 'Y',
165 'Ϋ' => 'Y', '' => 'Y', '' => 'Y', '' => 'Y', '' => 'Y', '' => 'Y',
166 '' => 'Y', '' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'P', 'Ω' => 'O',
167 'Ώ' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O',
168 '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O',
169 '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O', '' => 'O',
170 '' => 'O', 'α' => 'a', 'ά' => 'a', '' => 'a', '' => 'a', '' => 'a',
171 'ἃ' => 'a', 'ἄ' => 'a', '�
172 ' => 'a', '' => 'a', '' => 'a', '' => 'a',
173 'ᾁ' => 'a', 'ᾂ' => 'a', 'ᾃ' => 'a', 'ᾄ' => 'a', '�
174 ' => 'a', '' => 'a',
175 '' => 'a', '' => 'a', '' => 'a', '' => 'a', '' => 'a', '' => 'a',
176 '' => 'a', '' => 'a', '' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd',
177 'ε' => 'e', 'έ' => 'e', '' => 'e', '' => 'e', '' => 'e', '' => 'e',
178 '' => 'e', '' => 'e', '' => 'e', 'ζ' => 'z', 'η' => 'i', 'ή' => 'i',
179 '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i',
180 '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i',
181 '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i',
182 '' => 'i', '' => 'i', '' => 'i', '' => 'i', 'θ' => 't', 'ι' => 'i',
183 'ί' => 'i', 'ϊ' => 'i', 'ΐ' => 'i', '' => 'i', '' => 'i', '' => 'i',
184 '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i',
185 '' => 'i', '' => 'i', '' => 'i', '' => 'i', '' => 'i', 'κ' => 'k',
186 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => 'k', 'ο' => 'o', 'ό' => 'o',
187 'ὀ' => 'o', 'ὁ' => 'o', 'ὂ' => 'o', 'ὃ' => 'o', 'ὄ' => 'o', '�
188 ' => 'o',
189 '' => 'o', 'π' => 'p', 'ρ' => 'r', '' => 'r', '' => 'r', 'σ' => 's',
190 'ς' => 's', 'τ' => 't', '�
191 ' => 'y', 'ύ' => 'y', 'ϋ' => 'y', 'ΰ' => 'y',
192 '' => 'y', '' => 'y', '' => 'y', '' => 'y', '' => 'y', '' => 'y',
193 '' => 'y', '' => 'y', '' => 'y', '' => 'y', '' => 'y', '' => 'y',
194 '' => 'y', '' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'p', 'ω' => 'o',
195 'ώ' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o',
196 '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o',
197 '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o',
198 '' => 'o', '' => 'o', '' => 'o', '' => 'o', '' => 'o', 'А' => 'A',
199 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'E',
200 'Ж' => 'Z', 'З' => 'Z', 'И' => 'I', 'Й' => 'I', 'К' => 'K', 'Л' => 'L',
201 'М' => 'M', 'Н' => 'N', 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S',
202 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'K', 'Ц' => 'T', 'Ч' => 'C',
203 'Ш' => 'S', 'Щ' => 'S', 'Ы' => 'Y', 'Э' => 'E', 'Ю' => 'Y', 'Я' => 'Y',
204 'а' => 'A', 'б' => 'B', 'в' => 'V', 'г' => 'G', 'д' => 'D', 'е' => 'E',
205 'ё' => 'E', 'ж' => 'Z', 'з' => 'Z', 'и' => 'I', 'й' => 'I', 'к' => 'K',
206 'л' => 'L', 'м' => 'M', 'н' => 'N', 'о' => 'O', 'п' => 'P', 'р' => 'R',
207 'с' => 'S', 'т' => 'T', 'у' => 'U', 'ф' => 'F', '�
208 ' => 'K', 'ц' => 'T',
209 'ч' => 'C', 'ш' => 'S', 'щ' => 'S', 'ы' => 'Y', 'э' => 'E', 'ю' => 'Y',
210 'я' => 'Y', 'ð' => 'd', 'Ð' => 'D', 'þ' => 't', 'Þ' => 'T', '' => 'a',
211 '' => 'b', '' => 'g', '' => 'd', '' => 'e', '' => 'v', '' => 'z',
212 '' => 't', '' => 'i', '' => 'k', '' => 'l', '' => 'm', '' => 'n',
213 '' => 'o', '' => 'p', '' => 'z', '' => 'r', '' => 's', '' => 't',
214 '' => 'u', '' => 'p', '' => 'k', '' => 'g', '' => 'q', '' => 's',
215 '' => 'c', '' => 't', '' => 'd', '' => 't', '' => 'c', '' => 'k',
216 '' => 'j', '' => 'h', 'ţ' => 't', 'ʼ' => "'", '̧' => '', '' => 'h',
217 '' => "'", '' => "'", '' => 'u', '/' => '', 'ế' => 'e', '' => 'a',
218 '' => 'i', '' => 'a', '' => 'e', '' => 'i', '' => 'o', '' => 'e',
219 'ơ' => 'o', '' => 'a', '' => 'a', 'ư' => 'u', '' => 'a', '' => 'a',
220 '' => 'd', '' => 'H', '' => 'D', 'ș' => 's', 'ț' => 't', '' => 'o',
221 '' => 'a', 'ş' => 's', "'" => '', 'ու' => 'u', 'ա' => 'a', 'բ' => 'b',
222 'գ' => 'g', 'դ' => 'd', 'ե' => 'e', 'զ' => 'z', 'է' => 'e', 'ը' => 'y',
223 'թ' => 't', 'ժ' => 'zh', 'ի' => 'i', 'լ' => 'l', 'խ' => 'kh', 'ծ' => 'ts',
224 'կ' => 'k', 'հ' => 'h', 'ձ' => 'dz', 'ղ' => 'gh', 'ճ' => 'ch', 'մ' => 'm',
225 'յ' => 'y', 'ն' => 'n', 'շ' => 'sh', 'ո' => 'o', 'չ' => 'ch', 'պ' => 'p',
226 'ջ' => 'j', 'ռ' => 'r', 'ս' => 's', 'վ' => 'v', 'տ' => 't', 'ր' => 'r',
227 'ց' => 'ts', 'փ' => 'p', 'ք' => 'q', 'և' => 'ev', '�
228 ' => 'o', 'ֆ' => 'f',
229 ];
230 $arrayFrom = array_keys($transliterationTable);
231 $arrayTo = array_values($transliterationTable);
232 }
233
234 return str_replace($arrayFrom, $arrayTo, $value);
235 }
236
237 /**
238 * Transliterate a string to its closest ASCII representation.
239 *
240 * @param string $string
241 * @return string
242 * @see https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/Internet.php#L229
243 */
244 public static function transliterate($string)
245 {
246 if (0 === preg_match('/[^A-Za-z0-9_.]/', $string)) {
247 return $string;
248 }
249
250 $transString = static::ascii($string);
251
252 return preg_replace('/[^A-Za-z0-9_.]/u', '', $transString);
253 }
254
255 /**
256 * Get the portion of a string before the first occurrence of a given value.
257 *
258 * @param string $subject
259 * @param string $search
260 * @return string
261 */
262 public static function before($subject, $search)
263 {
264 if ($search === '') {
265 return $subject;
266 }
267
268 $result = strstr($subject, (string) $search, true);
269
270 return $result === false ? $subject : $result;
271 }
272
273 /**
274 * Get the portion of a string before the last occurrence of a given value.
275 *
276 * @param string $subject
277 * @param string $search
278 * @return string
279 */
280 public static function beforeLast($subject, $search)
281 {
282 if ($search === '') {
283 return $subject;
284 }
285
286 $pos = mb_strrpos($subject, $search);
287
288 if ($pos === false) {
289 return $subject;
290 }
291
292 return static::substr($subject, 0, $pos);
293 }
294
295 /**
296 * Get the portion of a string between two given values.
297 *
298 * @param string $subject
299 * @param string $from
300 * @param string $to
301 * @return string
302 */
303 public static function between($subject, $from, $to)
304 {
305 if ($from === '' || $to === '') {
306 return $subject;
307 }
308
309 return static::beforeLast(static::after($subject, $from), $to);
310 }
311
312 /**
313 * Get the smallest possible portion of a string between two given values.
314 *
315 * @param string $subject
316 * @param string $from
317 * @param string $to
318 * @return string
319 */
320 public static function betweenFirst($subject, $from, $to)
321 {
322 if ($from === '' || $to === '') {
323 return $subject;
324 }
325
326 return static::before(static::after($subject, $from), $to);
327 }
328
329 /**
330 * Convert a value to camel case.
331 *
332 * @param string $value
333 * @return string
334 */
335 public static function camel($value)
336 {
337 if (isset(static::$camelCache[$value])) {
338 return static::$camelCache[$value];
339 }
340
341 return static::$camelCache[$value] = lcfirst(static::studly($value));
342 }
343
344 /**
345 * Get the character at the specified index.
346 *
347 * @param string $subject
348 * @param int $index
349 * @return string|false
350 */
351 public static function charAt($subject, $index)
352 {
353 $length = mb_strlen($subject);
354
355 if ($index < 0 ? $index < -$length : $index > $length - 1) {
356 return false;
357 }
358
359 return mb_substr($subject, $index, 1);
360 }
361
362 /**
363 * Determine if a given string contains a given substring.
364 *
365 * @param string $haystack
366 * @param string|string[] $needles
367 * @return bool
368 */
369 public static function contains($haystack, $needles)
370 {
371 foreach ((array) $needles as $needle) {
372 if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
373 return true;
374 }
375 }
376
377 return false;
378 }
379
380 /**
381 * Determine if a given string contains all array values.
382 *
383 * @param string $haystack
384 * @param string[] $needles
385 * @return bool
386 */
387 public static function containsAll($haystack, array $needles)
388 {
389 foreach ($needles as $needle) {
390 if (! static::contains($haystack, $needle)) {
391 return false;
392 }
393 }
394
395 return true;
396 }
397
398 /**
399 * Determine if a given string ends with a given substring.
400 *
401 * @param string $haystack
402 * @param string|string[] $needles
403 * @return bool
404 */
405 public static function endsWith($haystack, $needles)
406 {
407 foreach ((array) $needles as $needle) {
408 if (
409 $needle !== '' && $needle !== null
410 && substr($haystack, -strlen($needle)) === (string) $needle
411 ) {
412 return true;
413 }
414 }
415
416 return false;
417 }
418
419 /**
420 * Cap a string with a single instance of a given value.
421 *
422 * @param string $value
423 * @param string $cap
424 * @return string
425 */
426 public static function finish($value, $cap)
427 {
428 $quoted = preg_quote($cap, '/');
429
430 return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
431 }
432
433 /**
434 * Determine if a given string matches a given pattern.
435 *
436 * @param string|array $pattern
437 * @param string $value
438 * @return bool
439 */
440 public static function is($pattern, $value)
441 {
442 $patterns = Arr::wrap($pattern);
443
444 $value = (string) $value;
445
446 if (empty($patterns)) {
447 return false;
448 }
449
450 foreach ($patterns as $pattern) {
451 $pattern = (string) $pattern;
452
453 // If the given value is an exact match we can of course return true right
454 // from the beginning. Otherwise, we will translate asterisks and do an
455 // actual pattern match against the two strings to see if they match.
456 if ($pattern == $value) {
457 return true;
458 }
459
460 $pattern = preg_quote($pattern, '#');
461
462 // Asterisks are translated into zero-or-more regular expression wildcards
463 // to make it convenient to check if the strings starts with the given
464 // pattern such as "library/*", making any string check convenient.
465 $pattern = str_replace('\*', '.*', $pattern);
466
467 if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
468 return true;
469 }
470 }
471
472 return false;
473 }
474
475 /**
476 * Converts a non-boolean value to boolean
477 * @param string|int $str
478 * @return bool|null
479 */
480 public static function toBool($str)
481 {
482 if (is_bool($str)) {
483 return $str;
484 }
485
486 $truthy = ['yes', 'on', 'true', '1', 1];
487
488 $falsy = ['no', 'off', 'false', '0', 0, ''];
489
490 if (in_array($str, $truthy, true)) {
491 return true;
492 } elseif (in_array($str, $falsy, true)) {
493 return false;
494 }
495 }
496
497 /**
498 * Determine if a given string is 7 bit ASCII.
499 *
500 * @param string $value
501 * @return bool
502 * @see https://developer.wordpress.org/reference/classes/requests_idnaencoder/is_ascii/
503 */
504 public static function isAscii($value)
505 {
506 return (preg_match('/(?:[^\x00-\x7F])/', $value) !== 1);
507 }
508
509 /**
510 * Checks if the word(s) are in capitalized form.
511 * @param string $str
512 * @param boolean $onlyFirst if true, checks only the first charatcer
513 * @return boolean
514 */
515 public static function isCapitalized($str, $onlyFirst = false)
516 {
517 if ($onlyFirst) {
518 return static::isUpper($str[0]);
519 }
520
521 if ($words = mb_split('\s', $str)) {
522 foreach ($words as $word) {
523 $isCap[] = static::isUpper($word[0]) && static::isLower(mb_substr($word, 1));
524 }
525 return count(array_filter($isCap)) === count($isCap);
526 }
527 }
528
529 /**
530 * Checks if the first character is in capital form.
531 *
532 * @param string $str
533 * @return boolean
534 */
535 public static function isCapital($str)
536 {
537 return static::isCapitalized($str, true);
538 }
539
540 /**
541 * Checks if the chracters are in upper case
542 *
543 * @param string $str
544 * @return boolean
545 */
546 public static function isUpper($str)
547 {
548 return $str === static::upper($str);
549 }
550
551 /**
552 * Checks if the chracters are in lower case
553 *
554 * @param string $str
555 * @return boolean
556 */
557 public static function isLower($str)
558 {
559 return $str === static::lower($str);
560 }
561
562 /**
563 * Checks if two words sounds alike
564 *
565 * @param string $str1
566 * @param string $str2
567 * @return bool
568 */
569 public static function soundsAlike($str1, $str2)
570 {
571 return soundex($str1) == soundex($str2);
572 }
573
574 /**
575 * Checks if two words are similar
576 *
577 * @param string $str1
578 * @param string $str2
579 *
580 * @return bool
581 */
582 public static function isSimilar($str1, $str2, $accuracy = 60)
583 {
584 $percent = static::similarityOf($str1, $str2);
585
586 return $percent > $accuracy;
587 }
588
589 /**
590 * Checks if two words are similar
591 *
592 * @param string $str1
593 * @param string $str2
594 *
595 * @return bool
596 */
597 public static function similarityOf($str1, $str2)
598 {
599 similar_text($str1, $str2, $percent);
600
601 return $percent;
602 }
603
604 /**
605 * Determine if a given value is valid JSON.
606 *
607 * @param mixed $value
608 * @return bool
609 */
610 public static function isJson($value)
611 {
612 if (! is_string($value)) {
613 return false;
614 }
615
616 try {
617 json_decode($value, true, 512, JSON_THROW_ON_ERROR);
618 } catch (\JsonException $e) {
619 return false;
620 }
621
622 return true;
623 }
624
625 /**
626 * Determine if a given value is a valid URL.
627 *
628 * @param mixed $value
629 * @param array $protocols
630 * @return bool
631 */
632 public static function isUrl($value, array $protocols = [])
633 {
634 if (! is_string($value)) {
635 return false;
636 }
637
638 $protocolList = empty($protocols)
639 ? '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'
640 : implode('|', $protocols);
641
642 /*
643 * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (5.0.7).
644 *
645 * (c) Fabien Potencier <fabien@symfony.com> http://symfony.com
646 */
647 $pattern = '~^
648 (FLUENT_PROTOCOLS):// # protocol
649 (((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+)@)? # basic auth
650 (
651 ([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
652 | # or
653 \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
654 | # or
655 \[
656 (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
657 \] # an IPv6 address
658 )
659 (:[0-9]+)? # a port (optional)
660 (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})* )* # a path
661 (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a query (optional)
662 (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a fragment (optional)
663 $~ixu';
664
665 return preg_match(str_replace('FLUENT_PROTOCOLS', $protocolList, $pattern), $value) > 0;
666 }
667
668 /**
669 * Determine if a given string is a valid UUID.
670 *
671 * @param string $value
672 * @return bool
673 */
674 public static function isUuid($value)
675 {
676 if (! is_string($value)) {
677 return false;
678 }
679
680 return preg_match(
681 '/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value
682 ) > 0;
683 }
684
685 /**
686 * Convert a string to kebab case.
687 *
688 * @param string $value
689 * @return string
690 */
691 public static function kebab($value)
692 {
693 return static::snake($value, '-');
694 }
695
696 /**
697 * Return the length of the given string.
698 *
699 * @param string $value
700 * @param string|null $encoding
701 * @return int
702 */
703 public static function length($value, $encoding = null)
704 {
705 if ($encoding) {
706 return mb_strlen($value, $encoding);
707 }
708
709 return mb_strlen($value);
710 }
711
712 /**
713 * Limit the number of characters in a string.
714 *
715 * @param string $value
716 * @param int $limit
717 * @param string $end
718 * @return string
719 */
720 public static function limit($value, $limit = 100, $end = '...')
721 {
722 if (mb_strwidth($value, 'UTF-8') <= $limit) {
723 return $value;
724 }
725
726 return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
727 }
728
729 /**
730 * Convert the given string to lower-case.
731 *
732 * @param string $value
733 * @return string
734 */
735 public static function lower($value)
736 {
737 return mb_strtolower($value, 'UTF-8');
738 }
739
740 /**
741 * Limit the number of words in a string.
742 *
743 * @param string $value
744 * @param int $words
745 * @param string $end
746 * @return string
747 */
748 public static function words($value, $words = 100, $end = '...')
749 {
750 preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
751
752 if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
753 return $value;
754 }
755
756 return rtrim($matches[0]).$end;
757 }
758
759 /**
760 * Masks a portion of a string with a repeated character.
761 *
762 * @param string $string
763 * @param string $character
764 * @param int $index
765 * @param int|null $length
766 * @param string $encoding
767 * @return string
768 */
769 public static function mask($string, $character, $index, $length = null, $encoding = 'UTF-8')
770 {
771 if ($character === '') {
772 return $string;
773 }
774
775 if (is_null($length) && PHP_MAJOR_VERSION < 8) {
776 $length = mb_strlen($string, $encoding);
777 }
778
779 $segment = mb_substr($string, $index, $length, $encoding);
780
781 if ($segment === '') {
782 return $string;
783 }
784
785 $strlen = mb_strlen($string, $encoding);
786 $startIndex = $index;
787
788 if ($index < 0) {
789 $startIndex = $index < -$strlen ? 0 : $strlen + $index;
790 }
791
792 $start = mb_substr($string, 0, $startIndex, $encoding);
793 $segmentLen = mb_strlen($segment, $encoding);
794 $end = mb_substr($string, $startIndex + $segmentLen);
795
796 return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$end;
797 }
798
799 /**
800 * Get the string matching the given pattern.
801 *
802 * @param string $pattern
803 * @param string $subject
804 * @return string
805 */
806 public static function match($pattern, $subject)
807 {
808 preg_match($pattern, $subject, $matches);
809
810 if (! $matches) {
811 return '';
812 }
813
814 return $matches[1] ?? $matches[0];
815 }
816
817 /**
818 * Determine if a given string matches a given pattern.
819 *
820 * @param string|iterable<string> $pattern
821 * @param string $value
822 * @return bool
823 */
824 public static function isMatch($pattern, $value)
825 {
826 $value = (string) $value;
827
828 if (! is_iterable($pattern)) {
829 $pattern = [$pattern];
830 }
831
832 foreach ($pattern as $pattern) {
833 $pattern = (string) $pattern;
834
835 if (preg_match($pattern, $value) === 1) {
836 return true;
837 }
838 }
839
840 return false;
841 }
842
843 /**
844 * Get the string matching the given pattern.
845 *
846 * @param string $pattern
847 * @param string $subject
848 * @return \FluentBooking\Framework\Support\Collection
849 */
850 public static function matchAll($pattern, $subject)
851 {
852 preg_match_all($pattern, $subject, $matches);
853
854 if (empty($matches[0])) {
855 return Helper::collect();
856 }
857
858 return Helper::collect($matches[1] ?? $matches[0]);
859 }
860
861 /**
862 * Pad both sides of a string with another.
863 *
864 * @param string $value
865 * @param int $length
866 * @param string $pad
867 * @return string
868 */
869 public static function padBoth($value, $length, $pad = ' ')
870 {
871 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_BOTH);
872 }
873
874 /**
875 * Pad the left side of a string with another.
876 *
877 * @param string $value
878 * @param int $length
879 * @param string $pad
880 * @return string
881 */
882 public static function padLeft($value, $length, $pad = ' ')
883 {
884 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_LEFT);
885 }
886
887 /**
888 * Pad the right side of a string with another.
889 *
890 * @param string $value
891 * @param int $length
892 * @param string $pad
893 * @return string
894 */
895 public static function padRight($value, $length, $pad = ' ')
896 {
897 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_RIGHT);
898 }
899
900 /**
901 * Parse a Class[@]method style callback into class and method.
902 *
903 * @param string $callback
904 * @param string|null $default
905 * @return array<int, string|null>
906 */
907 public static function parseCallback($callback, $default = null)
908 {
909 return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
910 }
911
912 /**
913 * Get the plural form of an English word.
914 *
915 * @param string $value
916 * @param int|array|\Countable $count
917 * @return string
918 */
919 public static function plural($value, $count = 2)
920 {
921 return Pluralizer::plural($value, $count);
922 }
923
924 /**
925 * Pluralize the last word of an English, studly caps case string.
926 *
927 * @param string $value
928 * @param int|array|\Countable $count
929 * @return string
930 */
931 public static function pluralStudly($value, $count = 2)
932 {
933 $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
934
935 $lastWord = array_pop($parts);
936
937 return implode('', $parts).self::plural($lastWord, $count);
938 }
939
940 /**
941 * Generate a more truly "random" alpha-numeric string.
942 *
943 * @param int $length
944 * @return string
945 */
946 public static function random($length = 16)
947 {
948 $string = '';
949
950 while (($len = strlen($string)) < $length) {
951 $size = $length - $len;
952
953 $bytes = random_bytes($size);
954
955 $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
956 }
957
958 return $string;
959 }
960
961 /**
962 * Repeat the given string.
963 *
964 * @param string $string
965 * @param int $times
966 * @return string
967 */
968 public static function repeat(string $string, int $times)
969 {
970 return str_repeat($string, $times);
971 }
972
973 /**
974 * Replace a given value in the string sequentially with an array.
975 *
976 * @param string $search
977 * @param array<int|string, string> $replace
978 * @param string $subject
979 * @return string
980 */
981 public static function replaceArray($search, array $replace, $subject)
982 {
983 $segments = explode($search, $subject);
984
985 $result = array_shift($segments);
986
987 foreach ($segments as $segment) {
988 $result .= (array_shift($replace) ?? $search).$segment;
989 }
990
991 return $result;
992 }
993
994 /**
995 * Replace the given value in the given string.
996 *
997 * @param string|string[] $search
998 * @param string|string[] $replace
999 * @param string|string[] $subject
1000 * @return string
1001 */
1002 public static function replace($search, $replace, $subject)
1003 {
1004 return str_replace($search, $replace, $subject);
1005 }
1006
1007 /**
1008 * Replace the first occurrence of a given value in the string.
1009 *
1010 * @param string $search
1011 * @param string $replace
1012 * @param string $subject
1013 * @return string
1014 */
1015 public static function replaceFirst($search, $replace, $subject)
1016 {
1017 if ($search === '') {
1018 return $subject;
1019 }
1020
1021 $position = strpos($subject, $search);
1022
1023 if ($position !== false) {
1024 return substr_replace($subject, $replace, $position, strlen($search));
1025 }
1026
1027 return $subject;
1028 }
1029
1030 /**
1031 * Replace the last occurrence of a given value in the string.
1032 *
1033 * @param string $search
1034 * @param string $replace
1035 * @param string $subject
1036 * @return string
1037 */
1038 public static function replaceLast($search, $replace, $subject)
1039 {
1040 if ($search === '') {
1041 return $subject;
1042 }
1043
1044 $position = strrpos($subject, $search);
1045
1046 if ($position !== false) {
1047 return substr_replace($subject, $replace, $position, strlen($search));
1048 }
1049
1050 return $subject;
1051 }
1052
1053 /**
1054 * Remove any occurrence of the given string in the subject.
1055 *
1056 * @param string|array<string> $search
1057 * @param string $subject
1058 * @param bool $caseSensitive
1059 * @return string
1060 */
1061 public static function remove($search, $subject, $caseSensitive = true)
1062 {
1063 $subject = $caseSensitive
1064 ? str_replace($search, '', $subject)
1065 : str_ireplace($search, '', $subject);
1066
1067 return $subject;
1068 }
1069
1070 /**
1071 * Reverse the given string.
1072 *
1073 * @param string $value
1074 * @return string
1075 */
1076 public static function reverse(string $value)
1077 {
1078 return implode(array_reverse(mb_str_split($value)));
1079 }
1080
1081 /**
1082 * Begin a string with a single instance of a given value.
1083 *
1084 * @param string $value
1085 * @param string $prefix
1086 * @return string
1087 */
1088 public static function start($value, $prefix)
1089 {
1090 $quoted = preg_quote($prefix, '/');
1091
1092 return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
1093 }
1094
1095 /**
1096 * Convert the given string to upper-case.
1097 *
1098 * @param string $value
1099 * @return string
1100 */
1101 public static function upper($value)
1102 {
1103 return mb_strtoupper($value, 'UTF-8');
1104 }
1105
1106 /**
1107 * Convert the given string to title case.
1108 *
1109 * @param string $value
1110 * @return string
1111 */
1112 public static function title($value)
1113 {
1114 return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
1115 }
1116
1117 /**
1118 * Convert the given string to title case for each word.
1119 *
1120 * @param string $value
1121 * @return string
1122 */
1123 public static function headline($value)
1124 {
1125 $parts = explode(' ', $value);
1126
1127 $parts = count($parts) > 1
1128 ? $parts = array_map([static::class, 'title'], $parts)
1129 : $parts = array_map([static::class, 'title'], static::ucsplit(implode('_', $parts)));
1130
1131 $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts));
1132
1133 return implode(' ', array_filter(explode('_', $collapsed)));
1134 }
1135
1136 /**
1137 * Get the singular form of an English word.
1138 *
1139 * @param string $value
1140 * @return string
1141 */
1142 public static function singular($value)
1143 {
1144 return Pluralizer::singular($value);
1145 }
1146
1147 /**
1148 * Generate a URL friendly "slug" from a given string.
1149 *
1150 * @param string $title
1151 * @return string
1152 * @see https://developer.wordpress.org/reference/functions/sanitize_title/
1153 */
1154 public static function slug($title, $fallback_title = '', $context = 'save')
1155 {
1156 return sanitize_title($title, $fallback_title, $context);
1157 }
1158
1159 /**
1160 * Convert a string to snake case.
1161 *
1162 * @param string $value
1163 * @param string $delimiter
1164 * @return string
1165 */
1166 public static function snake($value, $delimiter = '_')
1167 {
1168 $key = $value;
1169
1170 if (isset(static::$snakeCache[$key][$delimiter])) {
1171 return static::$snakeCache[$key][$delimiter];
1172 }
1173
1174 if (! ctype_lower($value)) {
1175 $value = preg_replace('/\s+/u', '', ucwords($value));
1176
1177 $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
1178 }
1179
1180 return static::$snakeCache[$key][$delimiter] = $value;
1181 }
1182
1183 /**
1184 * Remove all "extra" blank space from the given string.
1185 *
1186 * @param string $value
1187 * @return string
1188 */
1189 public static function squish($value)
1190 {
1191 return preg_replace('~(\s|\x{3164}|\x{1160})+~u', ' ', preg_replace('~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '', $value));
1192 }
1193
1194 /**
1195 * Determine if a given string starts with a given substring.
1196 *
1197 * @param string $haystack
1198 * @param string|string[] $needles
1199 * @return bool
1200 */
1201 public static function startsWith($haystack, $needles)
1202 {
1203 foreach ((array) $needles as $needle) {
1204 if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) {
1205 return true;
1206 }
1207 }
1208
1209 return false;
1210 }
1211
1212 /**
1213 * Convert a value to studly caps case.
1214 *
1215 * @param string $value
1216 * @return string
1217 */
1218 public static function studly($value)
1219 {
1220 $key = $value;
1221
1222 if (isset(static::$studlyCache[$key])) {
1223 return static::$studlyCache[$key];
1224 }
1225
1226 $words = explode(' ', static::replace(['-', '_'], ' ', $value));
1227
1228 $studlyWords = array_map(function ($word) {
1229 return static::ucfirst($word);
1230 }, $words);
1231
1232 return static::$studlyCache[$key] = implode($studlyWords);
1233 }
1234
1235 /**
1236 * Returns the portion of the string specified by the start and length parameters.
1237 *
1238 * @param string $string
1239 * @param int $start
1240 * @param int|null $length
1241 * @return string
1242 */
1243 public static function substr($string, $start, $length = null)
1244 {
1245 return mb_substr($string, $start, $length, 'UTF-8');
1246 }
1247
1248 /**
1249 * Returns the number of substring occurrences.
1250 *
1251 * @param string $haystack
1252 * @param string $needle
1253 * @param int $offset
1254 * @param int|null $length
1255 * @return int
1256 */
1257 public static function substrCount($haystack, $needle, $offset = 0, $length = null)
1258 {
1259 if (! is_null($length)) {
1260 return substr_count($haystack, $needle, $offset, $length);
1261 } else {
1262 return substr_count($haystack, $needle, $offset);
1263 }
1264 }
1265
1266 /**
1267 * Replace text within a portion of a string.
1268 *
1269 * @param string|array $string
1270 * @param string|array $replace
1271 * @param array|int $offset
1272 * @param array|int|null $length
1273 * @return string|array
1274 */
1275 public static function substrReplace($string, $replace, $offset = 0, $length = null)
1276 {
1277 if ($length === null) {
1278 $length = strlen($string);
1279 }
1280
1281 return substr_replace($string, $replace, $offset, $length);
1282 }
1283
1284 /**
1285 * Swap multiple keywords in a string with other keywords.
1286 *
1287 * @param array $map
1288 * @param string $subject
1289 * @return string
1290 */
1291 public static function swap(array $map, $subject)
1292 {
1293 return strtr($subject, $map);
1294 }
1295
1296 /**
1297 * Make a string's first character lowercase.
1298 *
1299 * @param string $string
1300 * @return string
1301 */
1302 public static function lcfirst($string)
1303 {
1304 return static::lower(static::substr($string, 0, 1)).static::substr($string, 1);
1305 }
1306
1307 /**
1308 * Make a string's first character uppercase.
1309 *
1310 * @param string $string
1311 * @return string
1312 */
1313 public static function ucfirst($string)
1314 {
1315 return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
1316 }
1317
1318 /**
1319 * Split a string into pieces by uppercase characters.
1320 *
1321 * @param string $string
1322 * @return array
1323 */
1324 public static function ucsplit($string)
1325 {
1326 return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY);
1327 }
1328
1329 /**
1330 * Get the number of words a string contains.
1331 *
1332 * @param string $string
1333 * @return int
1334 */
1335 public static function wordCount($string)
1336 {
1337 return str_word_count($string);
1338 }
1339
1340 /**
1341 * Wrap a string to a given number of characters.
1342 *
1343 * @param string $string
1344 * @param int $characters
1345 * @param string $break
1346 * @param bool $cutLongWords
1347 * @return string
1348 */
1349 public static function wordWrap(
1350 $string,
1351 $characters = 75,
1352 $break = "\n",
1353 $cutLongWords = false
1354 )
1355 {
1356 return wordwrap($string, $characters, $break, $cutLongWords);
1357 }
1358
1359 /**
1360 * Wrap the string with the given strings.
1361 *
1362 * @param string $value
1363 * @param string $before
1364 * @param string|null $after
1365 * @return string
1366 */
1367 public static function wrap($value, $before, $after = null)
1368 {
1369 return $before.$value.($after = $after ?: $before);
1370 }
1371
1372 /**
1373 * Generate a UUID (version 4).
1374 *
1375 * @return string
1376 */
1377 public static function uuid()
1378 {
1379 return wp_generate_uuid4();
1380 }
1381
1382 /**
1383 * Remove all strings from the casing caches.
1384 *
1385 * @return void
1386 */
1387 public static function flushCache()
1388 {
1389 static::$snakeCache = [];
1390 static::$camelCache = [];
1391 static::$studlyCache = [];
1392 }
1393 }
1394