PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.94
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.94
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Support / Str.php

Str.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 1.0.94, at vendor/wpfluent/framework/src/WPFluent/Support/Str.php

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