PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.41
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.41
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Support / Str.php

Str.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.41, at vendor/wpfluent/framework/src/WPFluent/Support/Str.php

1,545 lines 49.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\Framework\Support;
4
5 use Exception;
6 use JsonException;
7 use FluentBoards\Framework\Foundation\App;
8 use FluentBoards\Framework\Support\Helper;
9 use FluentBoards\Framework\Support\Stringable;
10 use FluentBoards\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 \FluentBoards\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 * Remove the given string(s) if it exists at the start of the haystack.
367 *
368 * @param string $subject
369 * @param string|array $needle
370 * @return string
371 */
372 public static function chopStart($subject, $needle)
373 {
374 foreach ((array) $needle as $n) {
375 if (str_starts_with($subject, $n)) {
376 return substr($subject, strlen($n));
377 }
378 }
379
380 return $subject;
381 }
382
383 /**
384 * Remove the given string(s) if it exists at the end of the haystack.
385 *
386 * @param string $subject
387 * @param string|array $needle
388 * @return string
389 */
390 public static function chopEnd($subject, $needle)
391 {
392 foreach ((array) $needle as $n) {
393 if (str_ends_with($subject, $n)) {
394 return substr($subject, 0, -strlen($n));
395 }
396 }
397
398 return $subject;
399 }
400
401 /**
402 * Determine if a given string contains a given substring.
403 *
404 * @param string $haystack
405 * @param string|string[] $needles
406 * @return bool
407 */
408 public static function contains($haystack, $needles)
409 {
410 foreach ((array) $needles as $needle) {
411 if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
412 return true;
413 }
414 }
415
416 return false;
417 }
418
419 /**
420 * Determine if a given string contains all array values.
421 *
422 * @param string $haystack
423 * @param string[] $needles
424 * @return bool
425 */
426 public static function containsAll($haystack, array $needles)
427 {
428 foreach ($needles as $needle) {
429 if (! static::contains($haystack, $needle)) {
430 return false;
431 }
432 }
433
434 return true;
435 }
436
437 /**
438 * Replace consecutive instances of a given character
439 * with a single character in the given string.
440 *
441 * @param string $string
442 * @param string $character
443 * @return string
444 */
445 public static function deduplicate(string $string, string $character = ' ')
446 {
447 return preg_replace(
448 '/'.preg_quote($character, '/').'+/u', $character, $string
449 );
450 }
451
452 /**
453 * Determine if a given string ends with a given substring.
454 *
455 * @param string $haystack
456 * @param string|string[] $needles
457 * @return bool
458 */
459 public static function endsWith($haystack, $needles)
460 {
461 foreach ((array) $needles as $needle) {
462 if (
463 $needle !== '' && $needle !== null
464 && substr($haystack, -strlen($needle)) === (string) $needle
465 ) {
466 return true;
467 }
468 }
469
470 return false;
471 }
472
473 /**
474 * Cap a string with a single instance of a given value.
475 *
476 * @param string $value
477 * @param string $cap
478 * @return string
479 */
480 public static function finish($value, $cap)
481 {
482 $quoted = preg_quote($cap, '/');
483
484 return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
485 }
486
487 /**
488 * Determine if a given string matches a given pattern.
489 *
490 * @param string|array $pattern
491 * @param string $value
492 * @return bool
493 */
494 public static function is($pattern, $value)
495 {
496 $patterns = Arr::wrap($pattern);
497
498 $value = (string) $value;
499
500 if (empty($patterns)) {
501 return false;
502 }
503
504 foreach ($patterns as $pattern) {
505 $pattern = (string) $pattern;
506
507 // If the given value is an exact match we can of course return true right
508 // from the beginning. Otherwise, we will translate asterisks and do an
509 // actual pattern match against the two strings to see if they match.
510 if ($pattern == $value) {
511 return true;
512 }
513
514 $pattern = preg_quote($pattern, '#');
515
516 // Asterisks are translated into zero-or-more regular expression wildcards
517 // to make it convenient to check if the strings starts with the given
518 // pattern such as "library/*", making any string check convenient.
519 $pattern = str_replace('\*', '.*', $pattern);
520
521 if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
522 return true;
523 }
524 }
525
526 return false;
527 }
528
529 /**
530 * Converts a non-boolean value to boolean
531 * @param string|int $str
532 * @return bool|null
533 */
534 public static function toBool($str)
535 {
536 if (is_bool($str)) {
537 return $str;
538 }
539
540 $truthy = ['yes', 'on', 'true', '1', 1];
541
542 $falsy = ['no', 'off', 'false', '0', 0, ''];
543
544 if (in_array($str, $truthy, true)) {
545 return true;
546 } elseif (in_array($str, $falsy, true)) {
547 return false;
548 }
549 }
550
551 /**
552 * Determine if a given string is 7 bit ASCII.
553 *
554 * @param string $value
555 * @return bool
556 * @see https://developer.wordpress.org/reference/classes/requests_idnaencoder/is_ascii/
557 */
558 public static function isAscii($value)
559 {
560 return (preg_match('/(?:[^\x00-\x7F])/', $value) !== 1);
561 }
562
563 /**
564 * Checks if the word(s) are in capitalized form.
565 * @param string $str
566 * @param boolean $onlyFirst if true, checks only the first charatcer
567 * @return boolean
568 */
569 public static function isCapitalized($str, $onlyFirst = false)
570 {
571 if ($onlyFirst) {
572 return static::isUpper($str[0]);
573 }
574
575 if ($words = mb_split('\s', $str)) {
576 foreach ($words as $word) {
577 $isCap[] = static::isUpper($word[0]) && static::isLower(mb_substr($word, 1));
578 }
579 return count(array_filter($isCap)) === count($isCap);
580 }
581 }
582
583 /**
584 * Checks if the first character is in capital form.
585 *
586 * @param string $str
587 * @return boolean
588 */
589 public static function isCapital($str)
590 {
591 return static::isCapitalized($str, true);
592 }
593
594 /**
595 * Checks if the chracters are in upper case
596 *
597 * @param string $str
598 * @return boolean
599 */
600 public static function isUpper($str)
601 {
602 return $str === static::upper($str);
603 }
604
605 /**
606 * Checks if the chracters are in lower case
607 *
608 * @param string $str
609 * @return boolean
610 */
611 public static function isLower($str)
612 {
613 return $str === static::lower($str);
614 }
615
616 /**
617 * Checks if two words sounds alike
618 *
619 * @param string $str1
620 * @param string $str2
621 * @return bool
622 */
623 public static function soundsAlike($str1, $str2)
624 {
625 return soundex($str1) == soundex($str2);
626 }
627
628 /**
629 * Checks if two words are similar
630 *
631 * @param string $str1
632 * @param string $str2
633 *
634 * @return bool
635 */
636 public static function isSimilar($str1, $str2, $accuracy = 60)
637 {
638 $percent = static::similarityOf($str1, $str2);
639
640 return $percent > $accuracy;
641 }
642
643 /**
644 * Checks if two words are similar
645 *
646 * @param string $str1
647 * @param string $str2
648 *
649 * @return bool
650 */
651 public static function similarityOf($str1, $str2)
652 {
653 similar_text($str1, $str2, $percent);
654
655 return $percent;
656 }
657
658 /**
659 * Determine if a given value is valid JSON.
660 *
661 * @param mixed $value
662 * @return bool
663 */
664 public static function isJson($value)
665 {
666 if (! is_string($value)) {
667 return false;
668 }
669
670 try {
671 json_decode($value, true, 512, JSON_THROW_ON_ERROR);
672 } catch (JsonException $e) {
673 return false;
674 }
675
676 return true;
677 }
678
679 /**
680 * Determine if a given value is a valid URL.
681 *
682 * @param mixed $value
683 * @param array $protocols
684 * @return bool
685 */
686 public static function isUrl($value, array $protocols = [])
687 {
688 if (! is_string($value)) {
689 return false;
690 }
691
692 $protocolList = empty($protocols)
693 ? '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'
694 : implode('|', $protocols);
695
696 /*
697 * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (5.0.7).
698 *
699 * (c) Fabien Potencier <fabien@symfony.com> http://symfony.com
700 */
701 $pattern = '~^
702 (FLUENT_PROTOCOLS):// # protocol
703 (((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+)@)? # basic auth
704 (
705 ([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
706 | # or
707 \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
708 | # or
709 \[
710 (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
711 \] # an IPv6 address
712 )
713 (:[0-9]+)? # a port (optional)
714 (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})* )* # a path
715 (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a query (optional)
716 (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a fragment (optional)
717 $~ixu';
718
719 return preg_match(str_replace('FLUENT_PROTOCOLS', $protocolList, $pattern), $value) > 0;
720 }
721
722 /**
723 * Determine if a given string is a valid UUID.
724 *
725 * @param string $value
726 * @return bool
727 */
728 public static function isUuid($value)
729 {
730 return wp_is_uuid($value);
731 }
732
733 /**
734 * Convert a string to kebab case.
735 *
736 * @param string $value
737 * @return string
738 */
739 public static function kebab($value)
740 {
741 return static::snake($value, '-');
742 }
743
744 /**
745 * Return the length of the given string.
746 *
747 * @param string $value
748 * @param string|null $encoding
749 * @return int
750 */
751 public static function length($value, $encoding = null)
752 {
753 if ($encoding) {
754 return mb_strlen($value, $encoding);
755 }
756
757 return mb_strlen($value);
758 }
759
760 /**
761 * Limit the number of characters in a string.
762 *
763 * @param string $value
764 * @param int $limit
765 * @param string $end
766 * @param bool $preserveWords
767 * @return string
768 */
769 public static function limit(
770 $value, $limit = 100, $end = '...', $preserveWords = false
771 )
772 {
773 if (mb_strwidth($value, 'UTF-8') <= $limit) {
774 return $value;
775 }
776
777 if (! $preserveWords) {
778 return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
779 }
780
781 $value = trim(preg_replace('/[\n\r]+/', ' ', strip_tags($value)));
782
783 $trimmed = rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8'));
784
785 if (mb_substr($value, $limit, 1, 'UTF-8') === ' ') {
786 return $trimmed.$end;
787 }
788
789 return preg_replace("/(.*)\s.*/", '$1', $trimmed).$end;
790 }
791
792 /**
793 * Convert the given string to lower-case.
794 *
795 * @param string $value
796 * @return string
797 */
798 public static function lower($value)
799 {
800 return mb_strtolower($value, 'UTF-8');
801 }
802
803 /**
804 * Limit the number of words in a string.
805 *
806 * @param string $value
807 * @param int $words
808 * @param string $end
809 * @return string
810 */
811 public static function words($value, $words = 100, $end = '...')
812 {
813 preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
814
815 if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
816 return $value;
817 }
818
819 return rtrim($matches[0]).$end;
820 }
821
822 /**
823 * Masks a portion of a string with a repeated character.
824 *
825 * @param string $string
826 * @param string $character
827 * @param int $index
828 * @param int|null $length
829 * @param string $encoding
830 * @return string
831 */
832 public static function mask(
833 $string, $character, $index, $length = null, $encoding = 'UTF-8'
834 )
835 {
836 if ($character === '') {
837 return $string;
838 }
839
840 if (is_null($length) && PHP_MAJOR_VERSION < 8) {
841 $length = mb_strlen($string, $encoding);
842 }
843
844 $segment = mb_substr($string, $index, $length, $encoding);
845
846 if ($segment === '') {
847 return $string;
848 }
849
850 $strlen = mb_strlen($string, $encoding);
851 $startIndex = $index;
852
853 if ($index < 0) {
854 $startIndex = $index < -$strlen ? 0 : $strlen + $index;
855 }
856
857 $start = mb_substr($string, 0, $startIndex, $encoding);
858 $segmentLen = mb_strlen($segment, $encoding);
859 $end = mb_substr($string, $startIndex + $segmentLen);
860
861 return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$end;
862 }
863
864 /**
865 * Get the string matching the given pattern.
866 *
867 * @param string $pattern
868 * @param string $subject
869 * @return string
870 */
871 public static function match($pattern, $subject)
872 {
873 preg_match($pattern, $subject, $matches);
874
875 if (! $matches) {
876 return '';
877 }
878
879 return $matches[1] ?? $matches[0];
880 }
881
882 /**
883 * Determine if a given string matches a given pattern.
884 *
885 * @param string|iterable<string> $pattern
886 * @param string $value
887 * @return bool
888 */
889 public static function isMatch($pattern, $value)
890 {
891 $value = (string) $value;
892
893 if (! is_iterable($pattern)) {
894 $pattern = [$pattern];
895 }
896
897 foreach ($pattern as $pattern) {
898 $pattern = (string) $pattern;
899
900 if (preg_match($pattern, $value) === 1) {
901 return true;
902 }
903 }
904
905 return false;
906 }
907
908 /**
909 * Get the string matching the given pattern.
910 *
911 * @param string $pattern
912 * @param string $subject
913 * @return \FluentBoards\Framework\Support\Collection
914 */
915 public static function matchAll($pattern, $subject)
916 {
917 preg_match_all($pattern, $subject, $matches);
918
919 if (empty($matches[0])) {
920 return Helper::collect();
921 }
922
923 return Helper::collect($matches[1] ?? $matches[0]);
924 }
925
926 /**
927 * Pad both sides of a string with another.
928 *
929 * @param string $value
930 * @param int $length
931 * @param string $pad
932 * @return string
933 */
934 public static function padBoth($value, $length, $pad = ' ')
935 {
936 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_BOTH);
937 }
938
939 /**
940 * Pad the left side of a string with another.
941 *
942 * @param string $value
943 * @param int $length
944 * @param string $pad
945 * @return string
946 */
947 public static function padLeft($value, $length, $pad = ' ')
948 {
949 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_LEFT);
950 }
951
952 /**
953 * Pad the right side of a string with another.
954 *
955 * @param string $value
956 * @param int $length
957 * @param string $pad
958 * @return string
959 */
960 public static function padRight($value, $length, $pad = ' ')
961 {
962 return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_RIGHT);
963 }
964
965 /**
966 * Parse a Class[@]method style callback into class and method.
967 *
968 * @param string $callback
969 * @param string|null $default
970 * @return array<int, string|null>
971 */
972 public static function parseCallback($callback, $default = null)
973 {
974 return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
975 }
976
977 /**
978 * Parse an integer from a string.
979 *
980 * @param string $value
981 * @return int|null
982 */
983 public static function parseInt($value)
984 {
985 if (preg_match('/[-+]?\d+/', $value, $matches)) {
986 return (int) $matches[0];
987 }
988 }
989
990 /**
991 * Parse a floasting point number from a string.
992 *
993 * @param string $value
994 * @return float|null
995 */
996 public static function parseFloat($value)
997 {
998 if (preg_match('/[-+]?\d*\.?\d+(e[-+]?\d+)?/i', $value, $matches)) {
999 return (float) $matches[0];
1000 }
1001 }
1002
1003 /**
1004 * Remove all non-numeric characters from a string.
1005 *
1006 * @param string $value
1007 * @return string
1008 */
1009 public static function parseNumber($value)
1010 {
1011 return preg_replace('/[^0-9]/', '', $value);
1012 }
1013
1014 /**
1015 * Get the plural form of an English word.
1016 *
1017 * @param string $value
1018 * @param int|array|\Countable $count
1019 * @return string
1020 */
1021 public static function plural($value, $count = 2)
1022 {
1023 return Pluralizer::plural($value, $count);
1024 }
1025
1026 /**
1027 * Pluralize the last word of an English, studly caps case string.
1028 *
1029 * @param string $value
1030 * @param int|array|\Countable $count
1031 * @return string
1032 */
1033 public static function pluralStudly($value, $count = 2)
1034 {
1035 $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
1036
1037 $lastWord = array_pop($parts);
1038
1039 return implode('', $parts).self::plural($lastWord, $count);
1040 }
1041
1042 /**
1043 * Find the multi-byte safe position of the first
1044 * occurrence of a given substring in a string.
1045 *
1046 * @param string $haystack
1047 * @param string $needle
1048 * @param int $offset
1049 * @param string|null $encoding
1050 * @return int|false
1051 */
1052 public static function position(
1053 $haystack, $needle, $offset = 0, $encoding = null
1054 )
1055 {
1056 return mb_strpos($haystack, (string) $needle, $offset, $encoding);
1057 }
1058
1059 /**
1060 * Generate a more truly "random" alpha-numeric string.
1061 *
1062 * @param int $length
1063 * @return string
1064 */
1065 public static function random($length = 16)
1066 {
1067 $string = '';
1068
1069 while (($len = strlen($string)) < $length) {
1070 $size = $length - $len;
1071
1072 $bytes = random_bytes($size);
1073
1074 $string .= substr(
1075 str_replace(
1076 ['/', '+', '='], '', base64_encode($bytes)
1077 ), 0, $size
1078 );
1079 }
1080
1081 return $string;
1082 }
1083
1084 /**
1085 * Repeat the given string.
1086 *
1087 * @param string $string
1088 * @param int $times
1089 * @return string
1090 */
1091 public static function repeat(string $string, int $times)
1092 {
1093 return str_repeat($string, $times);
1094 }
1095
1096 /**
1097 * Replace a given value in the string sequentially with an array.
1098 *
1099 * @param string $search
1100 * @param array<int|string, string> $replace
1101 * @param string $subject
1102 * @return string
1103 */
1104 public static function replaceArray($search, array $replace, $subject)
1105 {
1106 $segments = explode($search, $subject);
1107
1108 $result = array_shift($segments);
1109
1110 foreach ($segments as $segment) {
1111 $result .= (array_shift($replace) ?? $search).$segment;
1112 }
1113
1114 return $result;
1115 }
1116
1117 /**
1118 * Replace the given value in the given string.
1119 *
1120 * @param string|string[] $search
1121 * @param string|string[] $replace
1122 * @param string|string[] $subject
1123 * @return string
1124 */
1125 public static function replace($search, $replace, $subject)
1126 {
1127 return str_replace($search, $replace, $subject);
1128 }
1129
1130 /**
1131 * Replace the first occurrence of a given value in the string.
1132 *
1133 * @param string $search
1134 * @param string $replace
1135 * @param string $subject
1136 * @return string
1137 */
1138 public static function replaceFirst($search, $replace, $subject)
1139 {
1140 if ($search === '') {
1141 return $subject;
1142 }
1143
1144 $position = strpos($subject, $search);
1145
1146 if ($position !== false) {
1147 return substr_replace($subject, $replace, $position, strlen($search));
1148 }
1149
1150 return $subject;
1151 }
1152
1153 /**
1154 * Replace the last occurrence of a given value in the string.
1155 *
1156 * @param string $search
1157 * @param string $replace
1158 * @param string $subject
1159 * @return string
1160 */
1161 public static function replaceLast($search, $replace, $subject)
1162 {
1163 if ($search === '') {
1164 return $subject;
1165 }
1166
1167 $position = strrpos($subject, $search);
1168
1169 if ($position !== false) {
1170 return substr_replace($subject, $replace, $position, strlen($search));
1171 }
1172
1173 return $subject;
1174 }
1175
1176 /**
1177 * Remove any occurrence of the given string in the subject.
1178 *
1179 * @param string|array<string> $search
1180 * @param string $subject
1181 * @param bool $caseSensitive
1182 * @return string
1183 */
1184 public static function remove($search, $subject, $caseSensitive = true)
1185 {
1186 $subject = $caseSensitive
1187 ? str_replace($search, '', $subject)
1188 : str_ireplace($search, '', $subject);
1189
1190 return $subject;
1191 }
1192
1193 /**
1194 * Reverse the given string.
1195 *
1196 * @param string $value
1197 * @return string
1198 */
1199 public static function reverse(string $value)
1200 {
1201 return implode(array_reverse(mb_str_split($value)));
1202 }
1203
1204 /**
1205 * Begin a string with a single instance of a given value.
1206 *
1207 * @param string $value
1208 * @param string $prefix
1209 * @return string
1210 */
1211 public static function start($value, $prefix)
1212 {
1213 $quoted = preg_quote($prefix, '/');
1214
1215 return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
1216 }
1217
1218 /**
1219 * Convert the given string to upper-case.
1220 *
1221 * @param string $value
1222 * @return string
1223 */
1224 public static function upper($value)
1225 {
1226 return mb_strtoupper($value, 'UTF-8');
1227 }
1228
1229 /**
1230 * Convert the given string to title case.
1231 *
1232 * @param string $value
1233 * @return string
1234 */
1235 public static function title($value)
1236 {
1237 return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
1238 }
1239
1240 /**
1241 * Convert the given string to title case for each word.
1242 *
1243 * @param string $value
1244 * @return string
1245 */
1246 public static function headline($value)
1247 {
1248 $parts = explode(' ', $value);
1249
1250 $parts = count($parts) > 1
1251 ? $parts = array_map([static::class, 'title'], $parts)
1252 : $parts = array_map([static::class, 'title'], static::ucsplit(implode('_', $parts)));
1253
1254 $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts));
1255
1256 return implode(' ', array_filter(explode('_', $collapsed)));
1257 }
1258
1259 /**
1260 * Get the singular form of an English word.
1261 *
1262 * @param string $value
1263 * @return string
1264 */
1265 public static function singular($value)
1266 {
1267 return Pluralizer::singular($value);
1268 }
1269
1270 /**
1271 * Generate a URL friendly "slug" from a given string.
1272 *
1273 * @param string $title
1274 * @return string
1275 * @see https://developer.wordpress.org/reference/functions/sanitize_title/
1276 */
1277 public static function slug($title, $fallback_title = '', $context = 'save')
1278 {
1279 return sanitize_title($title, $fallback_title, $context);
1280 }
1281
1282 /**
1283 * Convert a string to snake case.
1284 *
1285 * @param string $value
1286 * @param string $delimiter
1287 * @return string
1288 */
1289 public static function snake($value, $delimiter = '_')
1290 {
1291 $key = $value;
1292
1293 if (isset(static::$snakeCache[$key][$delimiter])) {
1294 return static::$snakeCache[$key][$delimiter];
1295 }
1296
1297 if (! ctype_lower($value)) {
1298 $value = preg_replace('/\s+/u', '', ucwords($value));
1299
1300 $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
1301 }
1302
1303 return static::$snakeCache[$key][$delimiter] = $value;
1304 }
1305
1306 /**
1307 * Remove all "extra" blank space from the given string.
1308 *
1309 * @param string $value
1310 * @return string
1311 */
1312 public static function squish($value)
1313 {
1314 return preg_replace('~(\s|\x{3164}|\x{1160})+~u', ' ', preg_replace('~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '', $value));
1315 }
1316
1317 /**
1318 * Determine if a given string starts with a given substring.
1319 *
1320 * @param string $haystack
1321 * @param string|string[] $needles
1322 * @return bool
1323 */
1324 public static function startsWith($haystack, $needles)
1325 {
1326 foreach ((array) $needles as $needle) {
1327 if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) {
1328 return true;
1329 }
1330 }
1331
1332 return false;
1333 }
1334
1335 /**
1336 * Convert a value to studly caps case.
1337 *
1338 * @param string $value
1339 * @return string
1340 */
1341 public static function studly($value)
1342 {
1343 $key = $value;
1344
1345 if (isset(static::$studlyCache[$key])) {
1346 return static::$studlyCache[$key];
1347 }
1348
1349 $words = explode(' ', static::replace(['-', '_'], ' ', $value));
1350
1351 $studlyWords = array_map(function ($word) {
1352 return static::ucfirst($word);
1353 }, $words);
1354
1355 return static::$studlyCache[$key] = implode($studlyWords);
1356 }
1357
1358 /**
1359 * Returns the portion of the string specified by the start and length parameters.
1360 *
1361 * @param string $string
1362 * @param int $start
1363 * @param int|null $length
1364 * @return string
1365 */
1366 public static function substr($string, $start, $length = null)
1367 {
1368 return mb_substr($string, $start, $length, 'UTF-8');
1369 }
1370
1371 /**
1372 * Returns the number of substring occurrences.
1373 *
1374 * @param string $haystack
1375 * @param string $needle
1376 * @param int $offset
1377 * @param int|null $length
1378 * @return int
1379 */
1380 public static function substrCount(
1381 $haystack, $needle, $offset = 0, $length = null
1382 ) {
1383 if (! is_null($length)) {
1384 return substr_count($haystack, $needle, $offset, $length);
1385 } else {
1386 return substr_count($haystack, $needle, $offset);
1387 }
1388 }
1389
1390 /**
1391 * Replace text within a portion of a string.
1392 *
1393 * @param string|array $string
1394 * @param string|array $replace
1395 * @param array|int $offset
1396 * @param array|int|null $length
1397 * @return string|array
1398 */
1399 public static function substrReplace(
1400 $string, $replace, $offset = 0, $length = null
1401 ) {
1402 if ($length === null) {
1403 $length = strlen($string);
1404 }
1405
1406 return substr_replace($string, $replace, $offset, $length);
1407 }
1408
1409 /**
1410 * Swap multiple keywords in a string with other keywords.
1411 *
1412 * @param array $map
1413 * @param string $subject
1414 * @return string
1415 */
1416 public static function swap(array $map, $subject)
1417 {
1418 return strtr($subject, $map);
1419 }
1420
1421 /**
1422 * Make a string's first character lowercase.
1423 *
1424 * @param string $string
1425 * @return string
1426 */
1427 public static function lcfirst($string)
1428 {
1429 return static::lower(
1430 static::substr($string, 0, 1)
1431 ).static::substr($string, 1);
1432 }
1433
1434 /**
1435 * Make a string's first character uppercase.
1436 *
1437 * @param string $string
1438 * @return string
1439 */
1440 public static function ucfirst($string)
1441 {
1442 return static::upper(
1443 static::substr($string, 0, 1)
1444 ).static::substr($string, 1);
1445 }
1446
1447 /**
1448 * Split a string into pieces by uppercase characters.
1449 *
1450 * @param string $string
1451 * @return array
1452 */
1453 public static function ucsplit($string)
1454 {
1455 return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY);
1456 }
1457
1458 /**
1459 * Get the number of words a string contains.
1460 *
1461 * @param string $string
1462 * @return int
1463 */
1464 public static function wordCount($string)
1465 {
1466 return str_word_count($string);
1467 }
1468
1469 /**
1470 * Wrap a string to a given number of characters.
1471 *
1472 * @param string $string
1473 * @param int $characters
1474 * @param string $break
1475 * @param bool $cutLongWords
1476 * @return string
1477 */
1478 public static function wordWrap(
1479 $string,
1480 $characters = 75,
1481 $break = "\n",
1482 $cutLongWords = false
1483 )
1484 {
1485 return wordwrap($string, $characters, $break, $cutLongWords);
1486 }
1487
1488 /**
1489 * Wrap the string with the given strings.
1490 *
1491 * @param string $value
1492 * @param string $before
1493 * @param string|null $after
1494 * @return string
1495 */
1496 public static function wrap($value, $before, $after = null)
1497 {
1498 return $before.$value.($after = $after ?: $before);
1499 }
1500
1501 /**
1502 * Generate a UUID (version 4).
1503 *
1504 * @return string
1505 */
1506 public static function uuid()
1507 {
1508 return wp_generate_uuid4();
1509 }
1510
1511 /**
1512 * Increment a counter.
1513 * @return int
1514 */
1515 public static function incrementCounter($prefix = '')
1516 {
1517 return wp_unique_id($prefix);
1518 }
1519
1520 /**
1521 * Generate a deterministic unique id by a prefix.
1522 *
1523 * @param string $slug
1524 * @return string
1525 */
1526 public static function incrementBySlug($slug = '')
1527 {
1528 return wp_unique_prefixed_id(
1529 $slug = $slug ?: App::config()->get('app.slug')
1530 );
1531 }
1532
1533 /**
1534 * Remove all strings from the casing caches.
1535 *
1536 * @return void
1537 */
1538 public static function flushCache()
1539 {
1540 static::$snakeCache = [];
1541 static::$camelCache = [];
1542 static::$studlyCache = [];
1543 }
1544 }
1545