Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
152 / 152 |
|
100.00% |
11 / 11 |
CRAP | |
100.00% |
1 / 1 |
Str | |
100.00% |
152 / 152 |
|
100.00% |
11 / 11 |
68 | |
100.00% |
1 / 1 |
createSlug | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
createLinks | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
createRandom | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
createRandomAlphaNum | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
4 | |||
createRandomAlpha | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
createRandomNumeric | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
generateRandomString | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
__callStatic | |
100.00% |
77 / 77 |
|
100.00% |
1 / 1 |
36 | |||
convertFromCamelCase | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
5 | |||
convertToCamelCase | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
detectSeparator | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Utils; |
15 | |
16 | /** |
17 | * Pop utils string helper class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Utils |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 2.1.0 |
25 | */ |
26 | class Str |
27 | { |
28 | |
29 | /** |
30 | * Constants case type for random string generation |
31 | */ |
32 | const MIXEDCASE = 0; |
33 | const LOWERCASE = 1; |
34 | const UPPERCASE = 2; |
35 | |
36 | /** |
37 | * Characters for random string generation (certain characters omitted to eliminate confusion) |
38 | * @var array |
39 | */ |
40 | protected static array $randomChars = [ |
41 | 'abcdefghjkmnpqrstuvwxyz', |
42 | 'ABCDEFGHJKLMNPQRSTUVWXYZ', |
43 | '0123456789', |
44 | '!?#$%&@-_+*=,.:;()[]{}', |
45 | ]; |
46 | |
47 | /** |
48 | * Regex patterns & replacements for links |
49 | * @var array |
50 | */ |
51 | protected static array $linksRegex = [ |
52 | [ |
53 | 'pattern' => '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/m', |
54 | 'replacement' => '<a href="$0">$0</a>' |
55 | ], |
56 | [ |
57 | 'pattern' => '/[a-zA-Z0-9\.\-\_+%]+@[a-zA-Z0-9\-\_\.]+\.[a-zA-Z]{2,4}/m', |
58 | 'replacement' => '<a href="mailto:$0">$0</a>' |
59 | ] |
60 | ]; |
61 | |
62 | /** |
63 | * Allowed keywords for converting cases |
64 | * @var array |
65 | */ |
66 | protected static array $allowedCases = [ |
67 | 'titlecase', 'camelcase', 'kebabcase', 'dash', 'snakecase', 'underscore', 'namespace', 'path', 'url', 'uri' |
68 | ]; |
69 | |
70 | /** |
71 | * Convert the string into an SEO-friendly slug. |
72 | * |
73 | * @param string $string |
74 | * @param string $separator |
75 | * @return string |
76 | */ |
77 | public static function createSlug(string $string, string $separator = '-'): string |
78 | { |
79 | $string = str_replace(' ', $separator, preg_replace('/([^a-zA-Z0-9 \-\/])/', '', strtolower($string))); |
80 | $regex = '/' . $separator . '*' . $separator .'/'; |
81 | |
82 | return preg_replace($regex, $separator, $string); |
83 | } |
84 | |
85 | /** |
86 | * Convert any links in the string to HTML links. |
87 | * |
88 | * @param string $string |
89 | * @param array $attributes |
90 | * @return string |
91 | */ |
92 | public static function createLinks(string $string, array $attributes = []): string |
93 | { |
94 | foreach (self::$linksRegex as $regex) { |
95 | $replacement = $regex['replacement']; |
96 | |
97 | if (!empty($attributes)) { |
98 | $attribs = []; |
99 | foreach ($attributes as $attrib => $value) { |
100 | $attribs[] = $attrib . '="' . $value . '"'; |
101 | } |
102 | $replacement = str_replace('<a ', '<a ' . implode(' ', $attribs) . ' ', $replacement); |
103 | } |
104 | |
105 | $string = preg_replace($regex['pattern'], $replacement, $string); |
106 | } |
107 | |
108 | return $string; |
109 | } |
110 | |
111 | /** |
112 | * Generate a random string of a predefined length. |
113 | * |
114 | * @param int $length |
115 | * @param int $case |
116 | * @return string |
117 | */ |
118 | public static function createRandom(int $length, int $case = self::MIXEDCASE): string |
119 | { |
120 | $chars = self::$randomChars; |
121 | $charsets = []; |
122 | |
123 | switch ($case) { |
124 | case 1: |
125 | unset($chars[1]); |
126 | break; |
127 | case 2: |
128 | unset($chars[0]); |
129 | break; |
130 | } |
131 | |
132 | foreach ($chars as $key => $value) { |
133 | $charsets[] = str_split($value); |
134 | } |
135 | |
136 | return self::generateRandomString($length, $charsets); |
137 | } |
138 | |
139 | /** |
140 | * Generate a random alphanumeric string of a predefined length. |
141 | * |
142 | * @param int $length |
143 | * @param int $case |
144 | * @return string |
145 | */ |
146 | public static function createRandomAlphaNum(int $length, int $case = self::MIXEDCASE): string |
147 | { |
148 | $chars = self::$randomChars; |
149 | $charsets = []; |
150 | |
151 | switch ($case) { |
152 | case 1: |
153 | unset($chars[1]); |
154 | break; |
155 | case 2: |
156 | unset($chars[0]); |
157 | break; |
158 | } |
159 | unset($chars[3]); |
160 | |
161 | foreach ($chars as $key => $value) { |
162 | $charsets[] = str_split($value); |
163 | } |
164 | |
165 | return self::generateRandomString($length, $charsets); |
166 | } |
167 | |
168 | /** |
169 | * Generate a random alphabetical string of a predefined length. |
170 | * |
171 | * @param int $length |
172 | * @param int $case |
173 | * @return string |
174 | */ |
175 | public static function createRandomAlpha(int $length, int $case = self::MIXEDCASE): string |
176 | { |
177 | $chars = self::$randomChars; |
178 | $charsets = []; |
179 | |
180 | switch ($case) { |
181 | case 1: |
182 | unset($chars[1]); |
183 | break; |
184 | case 2: |
185 | unset($chars[0]); |
186 | break; |
187 | } |
188 | unset($chars[2]); |
189 | unset($chars[3]); |
190 | |
191 | foreach ($chars as $key => $value) { |
192 | $charsets[] = str_split($value); |
193 | } |
194 | |
195 | return self::generateRandomString($length, $charsets); |
196 | } |
197 | |
198 | /** |
199 | * Generate a random numeric string of a predefined length. |
200 | * |
201 | * @param int $length |
202 | * @return string |
203 | */ |
204 | public static function createRandomNumeric(int $length): string |
205 | { |
206 | return self::generateRandomString($length, [str_split(self::$randomChars[2])]); |
207 | } |
208 | |
209 | /** |
210 | * Generate characters based on length and character sets provided |
211 | * |
212 | * @param int $length |
213 | * @param array $charsets |
214 | * @return string |
215 | */ |
216 | public static function generateRandomString(int $length, array $charsets): string |
217 | { |
218 | $string = ''; |
219 | $indices = array_keys($charsets); |
220 | |
221 | for ($i = 0; $i < $length; $i++) { |
222 | $index = $indices[rand(0, (count($indices) - 1))]; |
223 | $subIndex = rand(0, (count($charsets[$index]) - 1)); |
224 | $string .= $charsets[$index][$subIndex]; |
225 | } |
226 | |
227 | return $string; |
228 | } |
229 | |
230 | /** |
231 | * Convert a string from one case to another |
232 | * |
233 | * @param string $name |
234 | * @param array $arguments |
235 | * @return string |
236 | */ |
237 | public static function __callStatic(string $name, array $arguments): string |
238 | { |
239 | [$from, $to] = explode('to', strtolower($name)); |
240 | $string = $arguments[0] ?? null; |
241 | $preserveCase = (array_key_exists(1, $arguments) && is_bool($arguments[1])) ? $arguments[1] : null; |
242 | $separator = null; |
243 | $prevSeparator = null; |
244 | $result = null; |
245 | |
246 | switch ($to) { |
247 | case 'titlecase': |
248 | case 'camelcase': |
249 | $preserveCase = true; |
250 | break; |
251 | case 'kebabcase': |
252 | case 'dash': |
253 | $separator = '-'; |
254 | if ($preserveCase === null) { |
255 | $preserveCase = false; |
256 | } |
257 | break; |
258 | case 'snakecase': |
259 | case 'underscore': |
260 | $separator = '_'; |
261 | if ($preserveCase === null) { |
262 | $preserveCase = false; |
263 | } |
264 | break; |
265 | case 'namespace': |
266 | $separator = '\\'; |
267 | if ($preserveCase === null) { |
268 | $preserveCase = true; |
269 | } |
270 | break; |
271 | case 'path': |
272 | $separator = DIRECTORY_SEPARATOR; |
273 | if ($preserveCase === null) { |
274 | $preserveCase = true; |
275 | } |
276 | break; |
277 | case 'uri': |
278 | case 'url': |
279 | $separator = '/'; |
280 | if ($preserveCase === null) { |
281 | $preserveCase = true; |
282 | } |
283 | break; |
284 | } |
285 | |
286 | switch ($from) { |
287 | case 'titlecase': |
288 | case 'camelcase': |
289 | $result = self::convertFromCamelCase($string, $separator, $preserveCase); |
290 | if ($to == 'titlecase') { |
291 | $result = ucfirst($result); |
292 | } |
293 | if ($to == 'camelcase') { |
294 | $result = lcfirst($result); |
295 | } |
296 | break; |
297 | case 'kebabcase': |
298 | case 'dash': |
299 | $prevSeparator = '-'; |
300 | break; |
301 | case 'snakecase': |
302 | case 'underscore': |
303 | $prevSeparator = '_'; |
304 | break; |
305 | case 'namespace': |
306 | $prevSeparator = '\\'; |
307 | break; |
308 | case 'path': |
309 | $prevSeparator = DIRECTORY_SEPARATOR; |
310 | break; |
311 | case 'url': |
312 | case 'uri': |
313 | $prevSeparator = '/'; |
314 | break; |
315 | } |
316 | |
317 | if ($result === null) { |
318 | switch ($to) { |
319 | case 'titlecase': |
320 | $result = ucfirst(self::convertToCamelCase($string, $prevSeparator)); |
321 | break; |
322 | case 'camelcase': |
323 | $result = lcfirst(self::convertToCamelCase($string, $prevSeparator)); |
324 | break; |
325 | default: |
326 | if ($preserveCase) { |
327 | $string = implode($prevSeparator, array_map('ucfirst', explode($prevSeparator, $string))); |
328 | } |
329 | $result = str_replace($prevSeparator, $separator, $string); |
330 | if ($preserveCase === false) { |
331 | $result = strtolower($result); |
332 | } |
333 | } |
334 | } |
335 | |
336 | return $result; |
337 | } |
338 | |
339 | /** |
340 | * Convert a camelCase string using the $separator value passed |
341 | * |
342 | * @param string $string |
343 | * @param ?string $separator |
344 | * @param bool $preserveCase |
345 | * @return string |
346 | */ |
347 | public static function convertFromCamelCase(string $string, ?string $separator = null, bool $preserveCase = false): string |
348 | { |
349 | $stringAry = str_split($string); |
350 | $converted = null; |
351 | |
352 | foreach ($stringAry as $i => $char) { |
353 | $converted .= ($i == 0) ? |
354 | $char : ((ctype_upper($char)) ? ($separator . $char) : $char); |
355 | } |
356 | |
357 | return ($preserveCase) ? $converted : strtolower($converted); |
358 | } |
359 | |
360 | /** |
361 | * Convert a camelCase string using the $separator value passed |
362 | * |
363 | * @param string $string |
364 | * @param ?string $separator |
365 | * @return string |
366 | */ |
367 | public static function convertToCamelCase(string $string, ?string $separator = null): string |
368 | { |
369 | if ($separator === null) { |
370 | $separator = self::detectSeparator($string); |
371 | } |
372 | $stringAry = explode($separator, $string); |
373 | $converted = null; |
374 | |
375 | foreach ($stringAry as $i => $word) { |
376 | $converted .= ($i == 0) ? $word : ucfirst($word); |
377 | } |
378 | |
379 | return $converted; |
380 | } |
381 | |
382 | /** |
383 | * Attempt to detect separator |
384 | * |
385 | * @param string $string |
386 | * @return string |
387 | */ |
388 | public static function detectSeparator(string $string): string |
389 | { |
390 | $separator = ''; |
391 | $separators = ['-', '_', '\\', '/', DIRECTORY_SEPARATOR]; |
392 | |
393 | foreach ($separators as $s) { |
394 | if (str_contains($string, $s)) { |
395 | return $s; |
396 | } |
397 | } |
398 | |
399 | return $separator; |
400 | } |
401 | |
402 | } |
403 |