nelson6e65 /
php_nml
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |