Passed
Push — master ( 2fc2d6...8a960a )
by Nelson
02:43 queued 01:00
created

Text::insert()   C

Complexity

Conditions 14
Paths 58

Size

Total Lines 58
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 14.6137

Importance

Changes 0
Metric Value
cc 14
eloc 38
c 0
b 0
f 0
nc 58
nop 3
dl 0
loc 58
ccs 35
cts 41
cp 0.8537
crap 14.6137
rs 6.2666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
The condition $r !== null is always true.
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
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
Bug introduced by
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 ignore-type  annotation

327
            while (($pos = strpos(/** @scrutinizer ignore-type */ $str, '?', $offset)) !== false) {
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
Bug introduced by
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 ignore-type  annotation

333
            return $options['clean'] ? static::cleanInsert(/** @scrutinizer ignore-type */ $str, $options) : $str;
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