1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * PHP: Nelson Martell Library file |
||||
5 | * |
||||
6 | * Copyright © 2015-2021 Nelson Martell (http://nelson6e65.github.io) |
||||
7 | * |
||||
8 | * Licensed under The MIT License (MIT) |
||||
9 | * For full copyright and license information, please see the LICENSE |
||||
10 | * Redistributions of files must retain the above copyright notice. |
||||
11 | * |
||||
12 | * @copyright 2015-2021 Nelson Martell |
||||
13 | * @link http://nelson6e65.github.io/php_nml/ |
||||
14 | * @since 0.7.0 |
||||
15 | * @license http://www.opensource.org/licenses/mit-license.php The MIT License (MIT) |
||||
16 | * */ |
||||
17 | |||||
18 | namespace NelsonMartell\Extensions; |
||||
19 | |||||
20 | use InvalidArgumentException; |
||||
21 | use NelsonMartell\IComparer; |
||||
22 | use NelsonMartell\StrictObject; |
||||
23 | |||||
24 | use function NelsonMartell\msg; |
||||
25 | use function NelsonMartell\typeof; |
||||
26 | |||||
27 | /** |
||||
28 | * Provides extension methods to handle strings. |
||||
29 | * This class is based on \Cake\Utility\Text of CakePHP(tm) class. |
||||
30 | * |
||||
31 | * @since 0.7.0 |
||||
32 | * @since 1.0.0 Remove `\Cake\Utility\Text` dependency. |
||||
33 | * @author Nelson Martell <[email protected]> |
||||
34 | * @see \Cake\Utility\Text::insert() |
||||
35 | * @link http://book.cakephp.org/3.0/en/core-libraries/text.html |
||||
36 | * */ |
||||
37 | class Text implements IComparer |
||||
38 | { |
||||
39 | /** |
||||
40 | * Replaces format elements in a string with the string representation of an |
||||
41 | * object matching the list of arguments specified. You can give as many |
||||
42 | * params as you need, or an array with values. |
||||
43 | * |
||||
44 | * ##Usage |
||||
45 | * Using numbers as placeholders (encloses between `{` and `}`), you can get |
||||
46 | * the matching string representation of each object given. Use `{0}` for |
||||
47 | * the fist object, `{1}` for the second, and so on: |
||||
48 | * |
||||
49 | * ```php |
||||
50 | * $format = '{0} is {1} years old, and have {2} cats.'; |
||||
51 | * echo Text::format($format, 'Bob', 65, 101); // 'Bob is 65 years old, and have 101 cats.' |
||||
52 | * ``` |
||||
53 | * |
||||
54 | * You can also use an array to give objects values: |
||||
55 | * |
||||
56 | * ```php |
||||
57 | * $format = '{0} is {1} years old, and have {2} cats.'; |
||||
58 | * $data = ['Bob', 65, 101]; |
||||
59 | * echo Text::format($format, $data); // 'Bob is 65 years old, and have 101 cats.' |
||||
60 | * ``` |
||||
61 | * |
||||
62 | * This is specially useful to be able to use non-numeric placeholders (named placeholders): |
||||
63 | * |
||||
64 | * ```php |
||||
65 | * $format = '{name} is {age} years old, and have {n} cats.'; |
||||
66 | * $data = ['name' => 'Bob', 'n' => 101, 'age' => 65]; |
||||
67 | * echo Text::format($format, $data); // 'Bob is 65 years old, and have 101 cats.' |
||||
68 | * ``` |
||||
69 | * |
||||
70 | * For numeric placeholders, yo can convert the array into a list of arguments. |
||||
71 | * |
||||
72 | * ```php |
||||
73 | * $format = '{0} is {1} years old, and have {2} cats.'; |
||||
74 | * $data = ['Bob', 65, 101]; |
||||
75 | * echo Text::format($format, ...$data); // 'Bob is 65 years old, and have 101 cats.' |
||||
76 | * ``` |
||||
77 | * |
||||
78 | * > Note: If objects are not convertible to string, it will throws and catchable exception |
||||
79 | * (`InvalidArgumentException`). |
||||
80 | * |
||||
81 | * @param string $format An string containing variable placeholders to be replaced. If you provide name |
||||
82 | * placeholders, you must pass the target array as |
||||
83 | * @param array<int, mixed> $args Object(s) to be replaced into $format placeholders. |
||||
84 | * You can provide one item only of type array for named placeholders replacement. For numeric placeholders, you |
||||
85 | * can still pass the array or convert it into arguments by using the '...' syntax instead. |
||||
86 | * |
||||
87 | * @return string |
||||
88 | * @throws InvalidArgumentException if $format is not an string or placeholder values are not string-convertibles. |
||||
89 | * @todo Implement formatting, like IFormatProvider or something like that. |
||||
90 | * @author Nelson Martell <[email protected]> |
||||
91 | */ |
||||
92 | 231 | public static function format($format, ...$args) |
|||
93 | { |
||||
94 | 231 | static $options = [ |
|||
95 | 231 | 'before' => '{', |
|||
96 | 231 | 'after' => '}', |
|||
97 | 231 | ]; |
|||
98 | |||||
99 | 231 | $originalData = $args; |
|||
100 | |||||
101 | // Make it compatible with named placeholders along numeric ones if passed only 1 array as argument |
||||
102 | 231 | if (count($args) === 1 && is_array($args[0])) { |
|||
103 | 217 | $originalData = $args[0]; |
|||
104 | } |
||||
105 | |||||
106 | 231 | $data = []; |
|||
107 | // Sanitize values to be convertibles into strings |
||||
108 | 231 | foreach ($originalData as $placeholder => $value) { |
|||
109 | 231 | $valueType = typeof($value); |
|||
110 | |||||
111 | 231 | if ($valueType->canBeString() === false) { |
|||
112 | 3 | $msg = 'Value for "{{0}}" placeholder is not convertible to string; "{1}" type given.'; |
|||
113 | 3 | throw new InvalidArgumentException(msg($msg, $placeholder, $valueType)); |
|||
114 | } |
||||
115 | |||||
116 | // This is to work-arround a bug in use of ``asort()`` function in ``Text::insert`` (at v3.2.5) |
||||
117 | // without SORT_STRING flag... by forcing value to be string. |
||||
118 | 231 | settype($value, 'string'); |
|||
119 | 231 | $data[$placeholder] = $value; |
|||
120 | } |
||||
121 | |||||
122 | 231 | return static::insert($format, $data, $options); |
|||
123 | } |
||||
124 | |||||
125 | /** |
||||
126 | * Ensures that object given is not null. If is `null`, throws and exception. |
||||
127 | * |
||||
128 | * @param mixed $obj Object to validate |
||||
129 | * |
||||
130 | * @return mixed Same object |
||||
131 | * @throws InvalidArgumentException if object is `null`. |
||||
132 | */ |
||||
133 | 147 | public static function ensureIsNotNull($obj) |
|||
134 | { |
||||
135 | 147 | if (is_null($obj)) { |
|||
136 | $msg = msg('Provided object must not be NULL.'); |
||||
137 | throw new InvalidArgumentException($msg); |
||||
138 | } |
||||
139 | |||||
140 | 147 | return $obj; |
|||
141 | } |
||||
142 | |||||
143 | /** |
||||
144 | * Ensures that object given is an string. Else, thows an exception |
||||
145 | * |
||||
146 | * @param mixed $obj Object to validate. |
||||
147 | * |
||||
148 | * @return string Same object given, but ensured that is an string. |
||||
149 | * @throws InvalidArgumentException if object is not an `string`. |
||||
150 | */ |
||||
151 | 147 | public static function ensureIsString($obj) |
|||
152 | { |
||||
153 | 147 | if (!is_string(static::ensureIsNotNull($obj))) { |
|||
154 | $msg = msg('Provided object must to be an string; "{0}" given.', typeof($obj)); |
||||
155 | throw new InvalidArgumentException($msg); |
||||
156 | } |
||||
157 | |||||
158 | 147 | return $obj; |
|||
159 | } |
||||
160 | |||||
161 | /** |
||||
162 | * Ensures that given string is not empty. |
||||
163 | * |
||||
164 | * @param string $string String to validate. |
||||
165 | * |
||||
166 | * @return string Same string given, but ensured that is not empty. |
||||
167 | * @throws InvalidArgumentException if string is null or empty. |
||||
168 | */ |
||||
169 | public static function ensureIsNotEmpty($string) |
||||
170 | { |
||||
171 | if (static::ensureIsString($string) === '') { |
||||
172 | $msg = msg('Provided string must not be empty.'); |
||||
173 | throw new InvalidArgumentException($msg); |
||||
174 | } |
||||
175 | |||||
176 | return $string; |
||||
177 | } |
||||
178 | |||||
179 | /** |
||||
180 | * Ensures that given string is not empty or whitespaces. |
||||
181 | * |
||||
182 | * @param string $string String to validate. |
||||
183 | * |
||||
184 | * @return string Same string given, but ensured that is not whitespaces. |
||||
185 | * @throws InvalidArgumentException if object is not an `string`. |
||||
186 | * @see \trim() |
||||
187 | */ |
||||
188 | public static function ensureIsNotWhiteSpaces($string) |
||||
189 | { |
||||
190 | if (trim(static::ensureIsNotEmpty($string)) === '') { |
||||
191 | $msg = msg('Provided string must not be white spaces.'); |
||||
192 | throw new InvalidArgumentException($msg); |
||||
193 | } |
||||
194 | |||||
195 | return $string; |
||||
196 | } |
||||
197 | |||||
198 | /** |
||||
199 | * Ensures that an string follows the PHP variables naming convention. |
||||
200 | * |
||||
201 | * @param string $string String to be ensured. |
||||
202 | * |
||||
203 | * @return string |
||||
204 | * @throws InvalidArgumentException if object is not an `string` or do not |
||||
205 | * follows the PHP variables naming convention. |
||||
206 | * |
||||
207 | * @see PropertyExtension::ensureIsValidName() |
||||
208 | */ |
||||
209 | 145 | public static function ensureIsValidVarName($string) |
|||
210 | { |
||||
211 | 145 | $pattern = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/'; |
|||
212 | |||||
213 | 145 | if (!preg_match($pattern, static::ensureIsString($string))) { |
|||
214 | $msg = msg('Provided string do not follows PHP variables naming convention: "{0}".', $string); |
||||
215 | throw new InvalidArgumentException($msg); |
||||
216 | } |
||||
217 | |||||
218 | 145 | return $string; |
|||
219 | } |
||||
220 | |||||
221 | |||||
222 | /** |
||||
223 | * {@inheritDoc} |
||||
224 | * |
||||
225 | * This methods is specific for the case when one of them are `string`. In other case, will fallback to |
||||
226 | * `Objects::compare()`.` You should use it directly instead of this method as comparation function |
||||
227 | * for `usort()`. |
||||
228 | * |
||||
229 | * @param string|mixed $left |
||||
230 | * @param string|mixed $right |
||||
231 | * |
||||
232 | * @return int|null |
||||
233 | * |
||||
234 | * @since 1.0.0 |
||||
235 | * @see Objects::compare() |
||||
236 | */ |
||||
237 | 22 | public static function compare($left, $right) |
|||
238 | { |
||||
239 | 22 | if (is_string($left)) { |
|||
240 | 22 | if (typeof($right)->isCustom()) { // String are minor than classes |
|||
241 | 5 | return -1; |
|||
242 | 18 | } elseif (typeof($right)->canBeString()) { |
|||
243 | 16 | return strnatcmp($left, $right); |
|||
244 | } else { |
||||
245 | 2 | return -1; |
|||
246 | } |
||||
247 | 6 | } elseif (is_string($right)) { |
|||
248 | 6 | $r = static::compare($right, $left); |
|||
249 | |||||
250 | 6 | if ($r !== null) { |
|||
0 ignored issues
–
show
introduced
by
![]() |
|||||
251 | 6 | $r *= -1; // Invert result |
|||
252 | } |
||||
253 | |||||
254 | 6 | return $r; |
|||
255 | } |
||||
256 | |||||
257 | 1 | return Objects::compare($left, $right); |
|||
258 | } |
||||
259 | |||||
260 | |||||
261 | |||||
262 | // ######################################################################## |
||||
263 | // |
||||
264 | // Methods based on CakePHP Utility (https://github.com/cakephp/utility) |
||||
265 | // |
||||
266 | // Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) |
||||
267 | // |
||||
268 | // ======================================================================== |
||||
269 | |||||
270 | |||||
271 | // ======================================================================== |
||||
272 | // Cake\Utility\Text |
||||
273 | // ------------------------------------------------------------------------ |
||||
274 | |||||
275 | |||||
276 | /** |
||||
277 | * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array |
||||
278 | * corresponds to a variable placeholder name in $str. |
||||
279 | * Example: |
||||
280 | * ``` |
||||
281 | * Text::insert(':name is :age years old.', ['name' => 'Bob', 'age' => '65']); |
||||
282 | * ``` |
||||
283 | * Returns: Bob is 65 years old. |
||||
284 | * |
||||
285 | * Available $options are: |
||||
286 | * |
||||
287 | * - before: The character or string in front of the name of the variable placeholder (Defaults to `:`) |
||||
288 | * - after: The character or string after the name of the variable placeholder (Defaults to null) |
||||
289 | * - escape: The character or string used to escape the before character / string (Defaults to `\`) |
||||
290 | * - format: A regex to use for matching variable placeholders. Default is: `/(?<!\\)\:%s/` |
||||
291 | * (Overwrites before, after, breaks escape / clean) |
||||
292 | * - clean: A boolean or array with instructions for Text::cleanInsert |
||||
293 | * |
||||
294 | * @param string $str A string containing variable placeholders |
||||
295 | * @param array $data A key => val array where each key stands for a placeholder variable name |
||||
296 | * to be replaced with val |
||||
297 | * @param array $options An array of options, see description above |
||||
298 | * @return string |
||||
299 | */ |
||||
300 | 231 | public static function insert(string $str, array $data, array $options = []): string |
|||
301 | { |
||||
302 | 231 | $defaults = [ |
|||
303 | 231 | 'before' => ':', |
|||
304 | 231 | 'after' => '', |
|||
305 | 231 | 'escape' => '\\', |
|||
306 | 231 | 'format' => null, |
|||
307 | 231 | 'clean' => false, |
|||
308 | 231 | ]; |
|||
309 | 231 | $options += $defaults; |
|||
310 | 231 | $format = $options['format']; |
|||
311 | 231 | $data = $data; |
|||
312 | 231 | if (empty($data)) { |
|||
313 | 14 | return $options['clean'] ? static::cleanInsert($str, $options) : $str; |
|||
314 | } |
||||
315 | |||||
316 | 231 | if (!isset($format)) { |
|||
317 | 231 | $format = sprintf( |
|||
318 | 231 | '/(?<!%s)%s%%s%s/', |
|||
319 | 231 | preg_quote($options['escape'], '/'), |
|||
320 | 231 | str_replace('%', '%%', preg_quote($options['before'], '/')), |
|||
321 | 231 | str_replace('%', '%%', preg_quote($options['after'], '/')) |
|||
322 | 231 | ); |
|||
323 | } |
||||
324 | |||||
325 | 231 | if (strpos($str, '?') !== false && is_numeric(key($data))) { |
|||
326 | $offset = 0; |
||||
327 | while (($pos = strpos($str, '?', $offset)) !== false) { |
||||
0 ignored issues
–
show
It seems like
$str can also be of type array ; however, parameter $haystack of strpos() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
328 | $val = array_shift($data); |
||||
329 | $offset = $pos + strlen($val); |
||||
330 | $str = substr_replace($str, $val, $pos, 1); |
||||
331 | } |
||||
332 | |||||
333 | return $options['clean'] ? static::cleanInsert($str, $options) : $str; |
||||
0 ignored issues
–
show
It seems like
$str can also be of type array ; however, parameter $str of NelsonMartell\Extensions\Text::cleanInsert() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
334 | } |
||||
335 | |||||
336 | 231 | $dataKeys = array_keys($data); |
|||
337 | 231 | $hashKeys = array_map('crc32', $dataKeys); |
|||
338 | /** @var array<string, string> $tempData */ |
||||
339 | 231 | $tempData = array_combine($dataKeys, $hashKeys); |
|||
340 | 231 | krsort($tempData); |
|||
341 | |||||
342 | 231 | foreach ($tempData as $key => $hashVal) { |
|||
343 | 231 | $key = sprintf($format, preg_quote($key, '/')); |
|||
344 | 231 | $str = preg_replace($key, $hashVal, $str); |
|||
345 | } |
||||
346 | /** @var array<string, mixed> $dataReplacements */ |
||||
347 | 231 | $dataReplacements = array_combine($hashKeys, array_values($data)); |
|||
348 | 231 | foreach ($dataReplacements as $tmpHash => $tmpValue) { |
|||
349 | 231 | $tmpValue = is_array($tmpValue) ? '' : $tmpValue; |
|||
350 | 231 | $str = str_replace($tmpHash, $tmpValue, $str); |
|||
351 | } |
||||
352 | |||||
353 | 231 | if (!isset($options['format']) && isset($options['before'])) { |
|||
354 | 231 | $str = str_replace($options['escape'] . $options['before'], $options['before'], $str); |
|||
355 | } |
||||
356 | |||||
357 | 231 | return $options['clean'] ? static::cleanInsert($str, $options) : $str; |
|||
358 | } |
||||
359 | |||||
360 | /** |
||||
361 | * Cleans up a Text::insert() formatted string with given $options depending on the 'clean' key in |
||||
362 | * $options. The default method used is text but html is also available. The goal of this function |
||||
363 | * is to replace all whitespace and unneeded markup around placeholders that did not get replaced |
||||
364 | * by Text::insert(). |
||||
365 | * |
||||
366 | * @param string $str String to clean. |
||||
367 | * @param array $options Options list. |
||||
368 | * @return string |
||||
369 | * @see \Cake\Utility\Text::insert() |
||||
370 | */ |
||||
371 | public static function cleanInsert(string $str, array $options): string |
||||
372 | { |
||||
373 | $clean = $options['clean']; |
||||
374 | if (!$clean) { |
||||
375 | return $str; |
||||
376 | } |
||||
377 | if ($clean === true) { |
||||
378 | $clean = ['method' => 'text']; |
||||
379 | } |
||||
380 | if (!is_array($clean)) { |
||||
381 | $clean = ['method' => $options['clean']]; |
||||
382 | } |
||||
383 | switch ($clean['method']) { |
||||
384 | case 'html': |
||||
385 | $clean += [ |
||||
386 | 'word' => '[\w,.]+', |
||||
387 | 'andText' => true, |
||||
388 | 'replacement' => '', |
||||
389 | ]; |
||||
390 | $kleenex = sprintf( |
||||
391 | '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i', |
||||
392 | preg_quote($options['before'], '/'), |
||||
393 | $clean['word'], |
||||
394 | preg_quote($options['after'], '/') |
||||
395 | ); |
||||
396 | $str = preg_replace($kleenex, $clean['replacement'], $str); |
||||
397 | if ($clean['andText']) { |
||||
398 | $options['clean'] = ['method' => 'text']; |
||||
399 | $str = static::cleanInsert($str, $options); |
||||
400 | } |
||||
401 | break; |
||||
402 | case 'text': |
||||
403 | $clean += [ |
||||
404 | 'word' => '[\w,.]+', |
||||
405 | 'gap' => '[\s]*(?:(?:and|or)[\s]*)?', |
||||
406 | 'replacement' => '', |
||||
407 | ]; |
||||
408 | |||||
409 | $kleenex = sprintf( |
||||
410 | '/(%s%s%s%s|%s%s%s%s)/', |
||||
411 | preg_quote($options['before'], '/'), |
||||
412 | $clean['word'], |
||||
413 | preg_quote($options['after'], '/'), |
||||
414 | $clean['gap'], |
||||
415 | $clean['gap'], |
||||
416 | preg_quote($options['before'], '/'), |
||||
417 | $clean['word'], |
||||
418 | preg_quote($options['after'], '/') |
||||
419 | ); |
||||
420 | $str = preg_replace($kleenex, $clean['replacement'], $str); |
||||
421 | break; |
||||
422 | } |
||||
423 | |||||
424 | return $str; |
||||
425 | } |
||||
426 | |||||
427 | |||||
428 | /** |
||||
429 | * Generate a random UUID version 4. |
||||
430 | * |
||||
431 | * Warning: This method should not be used as a random seed for any cryptographic operations. |
||||
432 | * Instead you should use the openssl or mcrypt extensions. |
||||
433 | * |
||||
434 | * It should also not be used to create identifiers that have security implications, such as |
||||
435 | * 'unguessable' URL identifiers. Instead you should use `Security::randomBytes()` for that. |
||||
436 | * |
||||
437 | * @see https://www.ietf.org/rfc/rfc4122.txt |
||||
438 | * @return string RFC 4122 UUID |
||||
439 | * @copyright Matt Farina MIT License https://github.com/lootils/uuid/blob/master/LICENSE |
||||
440 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) |
||||
441 | * |
||||
442 | * @since 1.0.0 Copied from https://github.com/cakephp/utility |
||||
443 | */ |
||||
444 | public static function uuid(): string |
||||
445 | { |
||||
446 | return sprintf( |
||||
447 | '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', |
||||
448 | // 32 bits for "time_low" |
||||
449 | random_int(0, 65535), |
||||
450 | random_int(0, 65535), |
||||
451 | // 16 bits for "time_mid" |
||||
452 | random_int(0, 65535), |
||||
453 | // 12 bits before the 0100 of (version) 4 for "time_hi_and_version" |
||||
454 | random_int(0, 4095) | 0x4000, |
||||
455 | // 16 bits, 8 bits for "clk_seq_hi_res", |
||||
456 | // 8 bits for "clk_seq_low", |
||||
457 | // two most significant bits holds zero and one for variant DCE1.1 |
||||
458 | random_int(0, 0x3fff) | 0x8000, |
||||
459 | // 48 bits for "node" |
||||
460 | random_int(0, 65535), |
||||
461 | random_int(0, 65535), |
||||
462 | random_int(0, 65535) |
||||
463 | ); |
||||
464 | } |
||||
465 | |||||
466 | |||||
467 | /** |
||||
468 | * Tokenizes a string using $separator, ignoring any instance of $separator that appears between |
||||
469 | * $leftBound and $rightBound. |
||||
470 | * |
||||
471 | * @param string $data The data to tokenize. |
||||
472 | * @param string $separator The token to split the data on. |
||||
473 | * @param string $leftBound The left boundary to ignore separators in. |
||||
474 | * @param string $rightBound The right boundary to ignore separators in. |
||||
475 | * @return string[] Array of tokens in $data. |
||||
476 | * |
||||
477 | * @since 1.0.0 Copied from https://github.com/cakephp/utility |
||||
478 | */ |
||||
479 | public static function tokenize( |
||||
480 | string $data, |
||||
481 | string $separator = ',', |
||||
482 | string $leftBound = '(', |
||||
483 | string $rightBound = ')' |
||||
484 | ): array { |
||||
485 | if (empty($data)) { |
||||
486 | return []; |
||||
487 | } |
||||
488 | |||||
489 | $depth = 0; |
||||
490 | $offset = 0; |
||||
491 | $buffer = ''; |
||||
492 | $results = []; |
||||
493 | $length = mb_strlen($data); |
||||
494 | $open = false; |
||||
495 | |||||
496 | while ($offset <= $length) { |
||||
497 | $tmpOffset = -1; |
||||
498 | $offsets = [ |
||||
499 | mb_strpos($data, $separator, $offset), |
||||
500 | mb_strpos($data, $leftBound, $offset), |
||||
501 | mb_strpos($data, $rightBound, $offset), |
||||
502 | ]; |
||||
503 | for ($i = 0; $i < 3; $i++) { |
||||
504 | if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset === -1)) { |
||||
505 | $tmpOffset = $offsets[$i]; |
||||
506 | } |
||||
507 | } |
||||
508 | if ($tmpOffset !== -1) { |
||||
509 | $buffer .= mb_substr($data, $offset, $tmpOffset - $offset); |
||||
510 | $char = mb_substr($data, $tmpOffset, 1); |
||||
511 | if (!$depth && $char === $separator) { |
||||
512 | $results[] = $buffer; |
||||
513 | $buffer = ''; |
||||
514 | } else { |
||||
515 | $buffer .= $char; |
||||
516 | } |
||||
517 | if ($leftBound !== $rightBound) { |
||||
518 | if ($char === $leftBound) { |
||||
519 | $depth++; |
||||
520 | } |
||||
521 | if ($char === $rightBound) { |
||||
522 | $depth--; |
||||
523 | } |
||||
524 | } else { |
||||
525 | if ($char === $leftBound) { |
||||
526 | if (!$open) { |
||||
527 | $depth++; |
||||
528 | $open = true; |
||||
529 | } else { |
||||
530 | $depth--; |
||||
531 | $open = false; |
||||
532 | } |
||||
533 | } |
||||
534 | } |
||||
535 | $tmpOffset += 1; |
||||
536 | $offset = $tmpOffset; |
||||
537 | } else { |
||||
538 | $results[] = $buffer . mb_substr($data, $offset); |
||||
539 | $offset = $length + 1; |
||||
540 | } |
||||
541 | } |
||||
542 | if (empty($results) && !empty($buffer)) { |
||||
543 | $results[] = $buffer; |
||||
544 | } |
||||
545 | |||||
546 | if (!empty($results)) { |
||||
547 | return array_map('trim', $results); |
||||
548 | } |
||||
549 | |||||
550 | return []; |
||||
551 | } |
||||
552 | |||||
553 | // ======================================================================== |
||||
554 | |||||
555 | |||||
556 | // ======================================================================== |
||||
557 | // Cake\Utility\Inflector |
||||
558 | // ------------------------------------------------------------------------ |
||||
559 | |||||
560 | /** |
||||
561 | * Returns the input lower_case_delimited_string as a CamelCasedString. |
||||
562 | * |
||||
563 | * @param string $string String to camelize |
||||
564 | * @param string $delimiter the delimiter in the input string |
||||
565 | * @return string CamelizedStringLikeThis. |
||||
566 | * @link https://book.cakephp.org/3.0/en/core-libraries/inflector.html#creating-camelcase-and-under-scored-forms |
||||
567 | */ |
||||
568 | 23 | public static function camelize(string $string, string $delimiter = '_'): string |
|||
569 | { |
||||
570 | 23 | $result = str_replace(' ', '', static::humanize($string, $delimiter)); |
|||
571 | 23 | return $result; |
|||
572 | } |
||||
573 | |||||
574 | |||||
575 | /** |
||||
576 | * Expects a CamelCasedInputString, and produces a lower_case_delimited_string |
||||
577 | * |
||||
578 | * @param string $string String to delimit |
||||
579 | * @param string $delimiter the character to use as a delimiter |
||||
580 | * @return string delimited string |
||||
581 | */ |
||||
582 | 23 | public static function delimit(string $string, string $delimiter = '_'): string |
|||
583 | { |
||||
584 | 23 | $result = mb_strtolower(preg_replace('/(?<=\\w)([A-Z])/', $delimiter . '\\1', $string)); |
|||
585 | 23 | return $result; |
|||
586 | } |
||||
587 | |||||
588 | |||||
589 | /** |
||||
590 | * Returns the input lower_case_delimited_string as 'A Human Readable String'. |
||||
591 | * (Underscores are replaced by spaces and capitalized following words.) |
||||
592 | * |
||||
593 | * @param string $string String to be humanized |
||||
594 | * @param string $delimiter the character to replace with a space |
||||
595 | * @return string Human-readable string |
||||
596 | * @link https://book.cakephp.org/3.0/en/core-libraries/inflector.html#creating-human-readable-forms |
||||
597 | */ |
||||
598 | 23 | public static function humanize(string $string, string $delimiter = '_'): string |
|||
599 | { |
||||
600 | 23 | $result = explode(' ', str_replace($delimiter, ' ', $string)); |
|||
601 | 23 | foreach ($result as &$word) { |
|||
602 | 23 | $word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1); |
|||
603 | } |
||||
604 | 23 | $result = implode(' ', $result); |
|||
605 | 23 | return $result; |
|||
606 | } |
||||
607 | |||||
608 | |||||
609 | /** |
||||
610 | * Returns the input CamelCasedString as an underscored_string. |
||||
611 | * |
||||
612 | * Also replaces dashes with underscores |
||||
613 | * |
||||
614 | * @param string $string CamelCasedString to be "underscorized" |
||||
615 | * @return string underscore_version of the input string |
||||
616 | * @link https://book.cakephp.org/3.0/en/core-libraries/inflector.html#creating-camelcase-and-under-scored-forms |
||||
617 | */ |
||||
618 | 23 | public static function underscore(string $string): string |
|||
619 | { |
||||
620 | 23 | return static::delimit(str_replace('-', '_', $string), '_'); |
|||
621 | } |
||||
622 | |||||
623 | /** |
||||
624 | * Returns camelBacked version of an underscored string. |
||||
625 | * |
||||
626 | * @param string $string String to convert. |
||||
627 | * @return string in variable form |
||||
628 | * @link https://book.cakephp.org/3.0/en/core-libraries/inflector.html#creating-variable-names |
||||
629 | */ |
||||
630 | 23 | public static function variable(string $string): string |
|||
631 | { |
||||
632 | 23 | $camelized = static::camelize(static::underscore($string)); |
|||
633 | 23 | $replace = strtolower(substr($camelized, 0, 1)); |
|||
634 | 23 | $result = $replace . substr($camelized, 1); |
|||
635 | 23 | return $result; |
|||
636 | } |
||||
637 | } |
||||
638 |