Passed
Push — master ( ce3a1e...c62c45 )
by Nikolaos
05:21
created

Str::ucwords()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 9
ccs 0
cts 0
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the Phalcon.
5
 *
6
 * (c) Phalcon Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Phalcon\Helper;
15
16
use function array_merge;
17
use function count_chars;
18
use function explode;
19
use function func_get_args;
20
use function function_exists;
21
use function implode;
22
use function is_array;
23
use function is_string;
24
use function mt_rand;
25
use function pathinfo;
26
use function preg_match_all;
27
use function preg_replace;
28
use function range;
29
use function rtrim;
30
use function str_replace;
31
use function str_split;
32
use function strlen;
33
use function strrev;
34
use function strtolower;
35
use function strtoupper;
36
use function substr;
37
use function substr_compare;
38
use function trim;
39
40
use function ucwords;
41
use function utf8_decode;
42
use const DIRECTORY_SEPARATOR;
43
use const MB_CASE_TITLE;
44
use const MB_CASE_UPPER;
45
use const PATHINFO_FILENAME;
46
47
/**
48
 * This class offers quick string functions throughout the framework
49
 */
50
class Str
51
{
52
    // Only alpha numeric characters [a-zA-Z0-9]
53
    public const RANDOM_ALNUM = 0;
54
    // Only alphabetical characters [azAZ]
55
    public const RANDOM_ALPHA = 1;
56
    // Only alpha numeric uppercase characters exclude similar
57
    // characters [2345679ACDEFHJKLMNPRSTUVWXYZ]
58
    public const RANDOM_DISTINCT = 5;
59
    // Only hexadecimal characters [0-9a-f]
60
    public const RANDOM_HEXDEC = 2;
61
    // Only numbers without 0 [1-9]
62
    public const RANDOM_NOZERO = 4;
63
    // Only numbers [0-9]
64
    public const RANDOM_NUMERIC = 3;
65
66
    /**
67
     * Concatenates strings using the separator only once without duplication in
68
     * places concatenation
69
     *
70
     * ```php
71
     * $str = Phalcon\Helper\Str::concat(
72
     *     "/",
73
     *     "/tmp/",
74
     *     "/folder_1/",
75
     *     "/folder_2",
76
     *     "folder_3/"
77
     * );
78
     *
79
     * echo $str;   // /tmp/folder_1/folder_2/folder_3/
80
     * ```
81
     *
82
     * @param string separator
83
     * @param string a
84
     * @param string b
85
     * @param string ...N
86
     *
87
     * @return string
88
     * @throws Exception
89
     */
90
    final public static function concat(): string
91
    {
92
        $arguments = func_get_args();
93
94
        if (count($arguments) < 3) {
95
            throw new Exception(
96
                "concat needs at least three parameters"
97
            );
98
        }
99
100
        $delimiter = Arr::first($arguments);
101
        $arguments = Arr::sliceRight($arguments);
102
        $first     = Arr::first($arguments);
103
        $last      = Arr::last($arguments);
104
        $prefix    = "";
105
        $suffix    = "";
106
        $data      = [];
107
108
        if (self::startsWith($first, $delimiter)) {
109
            $prefix = $delimiter;
110
        }
111
112
        if (self::endsWith($last, $delimiter)) {
113
            $suffix = $delimiter;
114
        }
115
116
        foreach ($arguments as $argument) {
117
            $data[] = trim($argument, $delimiter);
118
        }
119
120
        return $prefix . implode($delimiter, $data) . $suffix;
121
    }
122
123
    /**
124
     * Returns number of vowels in provided string. Uses a regular expression
125
     * to count the number of vowels (A, E, I, O, U) in a string.
126
     *
127
     * @param string $text
128
     *
129
     * @return int
130
     */
131
    final public static function countVowels(string $text): int
132
    {
133
        preg_match_all("/[aeiou]/i", $text, $matches);
134
135
        return count($matches[0]);
136
    }
137
138
    /**
139
     * Decapitalizes the first letter of the string and then adds it with rest
140
     * of the string. Omit the upperRest parameter to keep the rest of the
141
     * string intact, or set it to true to convert to uppercase.
142
     *
143
     * @param string $text
144
     * @param bool   $upperRest
145
     * @param string $encoding
146
     *
147
     * @return string
148
     */
149
    final public static function decapitalize(
150
        string $text,
151
        bool $upperRest = false,
152
        string $encoding = "UTF-8"
153
    ): string {
154
        $substr = mb_substr($text, 1);
155
156
        if ($upperRest) {
157
            $suffix = mb_strtoupper($substr, $encoding);
158
        } else {
159
            $suffix = $substr;
160
        }
161
162
        return mb_strtolower(mb_substr($text, 0, 1), $encoding) . $suffix;
163
    }
164
165
    /**
166
     * Removes a number from a string or decrements that number if it is already
167
     * defined
168
     *
169
     * ```php
170
     * use Phalcon\Helper\Str;
171
     *
172
     * echo Str::decrement("a_1");    // "a"
173
     * echo Str::decrement("a_2");  // "a_1"
174
     * ```
175
     *
176
     * @param string $text
177
     * @param string $separator
178
     *
179
     * @return string
180
     */
181
    final public static function decrement(
182
        string $text,
183
        string $separator = "_"
184
    ): string {
185
        $number = 0;
186
        $parts  = explode($separator, $text);
187
188
        if (isset($parts[1])) {
189
            $number = $parts[1];
190
            $number--;
191
            if ($number <= 0) {
192
                return $parts[0];
193
            }
194
        }
195
196
        return $parts[0] . $separator . $number;
197
    }
198
199
    /**
200
     * Accepts a file name (without extension) and returns a calculated
201
     * directory structure with the filename in the end
202
     *
203
     * @param string $file
204
     *
205
     * @return string
206
     */
207
    final public static function dirFromFile(string $file): string
208
    {
209
        $name  = pathinfo($file, PATHINFO_FILENAME);
210
        $start = substr($name, 0, -2);
211
212
        if (!$start) {
213
            $start = substr($name, 0, 1);
214
        }
215
216
        return implode('/', str_split($start, 2)) . '/';
217
    }
218
219
    /**
220
     * Accepts a directory name and ensures that it ends with
221
     * DIRECTORY_SEPARATOR
222
     *
223
     * @param string $directory
224
     *
225
     * @return string
226
     */
227
    final public static function dirSeparator(string $directory): string
228
    {
229
        return rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
230
    }
231
232
    /**
233
     * Check if a string ends with a given string
234
     *
235
     * @param string $haystack
236
     * @param string $needle
237
     * @param bool   $ignoreCase
238
     *
239
     * @return bool
240
     */
241
    final public static function endsWith(
242
        string $haystack,
243
        string $needle,
244
        bool $ignoreCase = true
245
    ): bool {
246
        if ('' === $haystack) {
247
            return false;
248
        }
249
250
        return 0 === substr_compare(
251
            $haystack,
252
            $needle,
253
            -strlen($needle),
254
            strlen($needle),
255
            $ignoreCase
256
        );
257
    }
258
259
    /**
260
     * Returns the first string there is between the strings from the
261
     * parameter start and end.
262
     *
263
     * @param string $text
264
     * @param string $start
265
     * @param string $end
266
     *
267
     * @return string
268
     */
269
    final public static function firstBetween(
270
        string $text,
271
        string $start,
272
        string $end
273
    ): string {
274
        $result = mb_strstr($text, $start);
275
        $result = (false === $result) ? '' : $result;
276
        $result = mb_strstr($result, $end, true);
277
        $result = (false === $result) ? '' : $result;
278
279
        return trim($result, $start . $end);
280
    }
281
282
    /**
283
     * Changes a text to a URL friendly one
284
     *
285
     * @param string     $text
286
     * @param string     $separator
287
     * @param bool       $lowercase
288
     * @param mixed|null $replace
289
     *
290
     * @return string
291
     * @throws Exception
292
     */
293
    public function friendly(
294
        string $text,
295
        string $separator = "-",
296
        bool $lowercase = true,
297
        $replace = null
298
    ): string {
299
300
        if (null !== $replace) {
301
            if (!is_array($replace) && !is_string($replace)) {
302
                throw new Exception(
303
                    "Parameter replace must be an array or a string"
304
                );
305
            }
306
307
            if (is_string($replace)) {
308
                $replace = [$replace];
309
            }
310
311
            $text = str_replace($replace, " ", $text);
312
        }
313
314
        $friendly = preg_replace(
315
            "/[^a-zA-Z0-9\\/_|+ -]/",
316
            "",
317
            $text
318
        );
319
320
        if ($lowercase) {
321
            $friendly = strtolower($friendly);
322
        }
323
324
        $friendly = preg_replace("/[\\/_|+ -]+/", $separator, $friendly);
325
        $friendly = trim($friendly, $separator);
326
327
        return $friendly;
328
    }
329
330
    /**
331
     * Makes an underscored or dashed phrase human-readable
332
     *
333
     * @param string $text
334
     *
335
     * @return string
336
     */
337
    final public static function humanize(string $text): string
338
    {
339
        $result = preg_replace('#[_-]+#', ' ', trim($text));
340
341
        return (null === $result) ? '' : $result;
342
    }
343
344
    /**
345
     * Lets you determine whether or not a string includes another string.
346
     *
347
     * @param string $haystack
348
     * @param string $needle
349
     *
350
     * @return bool
351
     */
352
    final public static function includes(
353
        string $haystack,
354
        string $needle
355
    ): bool {
356
        return false !== mb_strpos($haystack, $needle);
357
    }
358
359
    /**
360
     * Adds a number to a string or increment that number if it already is
361
     * defined
362
     *
363
     * @param string $text
364
     * @param string $separator
365
     *
366
     * @return string
367
     */
368
    final public static function increment(
369
        string $text,
370
        string $separator = "_"
371
    ): string {
372
        $parts  = explode($separator, $text);
373
        $number = 1;
374
375
        if (isset($parts[1])) {
376
            $number = ((int) $parts[1]) + 1;
377
        }
378
379
        return $parts[0] . $separator . $number;
380
    }
381
382
    /**
383
     * Compare two strings and returns true if both strings are anagram,
384
     * false otherwise.
385
     *
386
     * @param string $first
387
     * @param string $second
388
     *
389
     * @return bool
390
     */
391
    final public static function isAnagram(string $first, string $second): bool
392
    {
393
        return count_chars($first, 1) === count_chars($second, 1);
394
    }
395
396
    /**
397
     * Returns true if the given string is lower case, false otherwise.
398
     *
399
     * @param string $text
400
     * @param string $encoding
401
     *
402
     * @return bool
403
     */
404
    final public static function isLower(
405
        string $text,
406
        string $encoding = "UTF-8"
407
    ): bool {
408
        return $text === self::lower($text, $encoding);
409
    }
410
411
    /**
412
     * Returns true if the given string is a palindrome, false otherwise.
413
     *
414
     * @param string $text
415
     *
416
     * @return bool
417
     */
418
    final public static function isPalindrome(string $text): bool
419
    {
420
        return strrev($text) === $text;
421
    }
422
423
    /**
424
     * Returns true if the given string is upper case, false otherwise.
425
     *
426
     * @param string $text
427
     * @param string $encoding
428
     *
429
     * @return bool
430
     */
431
    final public static function isUpper(
432
        string $text,
433
        string $encoding = "UTF-8"
434
    ): bool {
435
        return $text === self::upper($text, $encoding);
436
    }
437
438
    /**
439
     * Calculates the length of the string. Uses mbstring if present
440
     *
441
     * @param string $text
442
     * @param string $encoding
443
     *
444
     * @return int
445
     */
446
    final public static function len(
447
        string $text,
448
        string $encoding = "UTF-8"
449
    ): int {
450
        if (function_exists("mb_strlen")) {
451
            return mb_strlen($text, $encoding);
452
        }
453
454
        return strlen(utf8_decode($text));
455
    }
456
457
    /**
458
     * Lowercases a string, this function makes use of the mbstring extension if
459
     * available
460
     *
461
     * @param string $text
462
     * @param string $encoding
463
     *
464
     * @return string
465
     */
466
    final public static function lower(
467
        string $text,
468
        string $encoding = "UTF-8"
469
    ): string {
470
        if (function_exists("mb_convert_case")) {
471
            return mb_convert_case($text, MB_CASE_LOWER, $encoding);
472
        }
473
474
        return strtolower(utf8_decode($text));
475
    }
476
477
    /**
478
     * Generates a random string based on the given type. Type is one of the
479
     * RANDOM_* constants
480
     *
481
     * @param int $type
482
     * @param int $length
483
     *
484
     * @return string
485
     */
486
    final public static function random(
487
        int $type = self::RANDOM_ALNUM,
488
        int $length = 8
489
    ): string {
490
        $text  = "";
491
        $type  = ($type < 0 || $type > 5) ? self::RANDOM_ALNUM : $type;
492
        $pools = [
493
            self::RANDOM_ALPHA    => array_merge(
494
                range('a', 'z'),
495
                range('A', 'Z')
496
            ),
497
            self::RANDOM_HEXDEC   => array_merge(
498
                range(0, 9),
499
                range('a', 'f')
500
            ),
501
            self::RANDOM_NUMERIC  => range(0, 9),
502
            self::RANDOM_NOZERO   => range(1, 9),
503
            self::RANDOM_DISTINCT => str_split('2345679ACDEFHJKLMNPRSTUVWXYZ'),
504
            self::RANDOM_ALNUM    => array_merge(
505
                range(0, 9),
506
                range('a', 'z'),
507
                range('A', 'Z')
508
            ),
509
        ];
510
511
        $end = count($pools[$type]) - 1;
512
513
        while (strlen($text) < $length) {
514
            $text .= $pools[$type][mt_rand(0, $end)];
515
        }
516
517
        return $text;
518
    }
519
520
    /**
521
     * Reduces multiple slashes in a string to single slashes
522
     *
523
     * @param string $text
524
     *
525
     * @return string
526
     */
527
    final public static function reduceSlashes(string $text): string
528
    {
529
        $result = preg_replace('#(?<!:)//+#', '/', $text);
530
531
        return (null === $result) ? '' : $result;
532
    }
533
534
    /**
535
     * Check if a string starts with a given string
536
     *
537
     * @param string $haystack
538
     * @param string $needle
539
     * @param bool   $ignoreCase
540
     *
541
     * @return bool
542
     */
543
    final public static function startsWith(
544
        string $haystack,
545
        string $needle,
546
        bool $ignoreCase = true
547
    ): bool {
548
        if ('' === $haystack) {
549
            return false;
550
        }
551
552
        return 0 === substr_compare(
553
            $haystack,
554
            $needle,
555
            0,
556
            strlen($needle),
557
            $ignoreCase
558
        );
559
    }
560
561
    /**
562
     * Makes a phrase underscored instead of spaced
563
     *
564
     * @param string $text
565
     * @param string $encoding
566
     *
567
     * @return string
568
     */
569
    final public static function ucwords(
570
        string $text,
571
        string $encoding = "UTF-8"
572
    ): string {
573
        if (function_exists("mb_convert_case")) {
574
            return mb_convert_case($text, MB_CASE_TITLE, $encoding);
575
        }
576
577
        return ucwords(utf8_decode($text));
578
    }
579
580
    /**
581
     * Makes a phrase underscored instead of spaced
582
     *
583
     * @param string $text
584
     *
585
     * @return string
586
     */
587
    final public static function underscore(string $text): string
588
    {
589
        $result = preg_replace('#\s+#', '_', trim($text));
590
591
        return (null === $result) ? '' : $result;
592
    }
593
594
    /**
595
     * Uppercases a string, this function makes use of the mbstring extension if
596
     * available
597
     *
598
     * @param string $text
599
     * @param string $encoding
600
     *
601
     * @return string
602
     */
603
    final public static function upper(
604
        string $text,
605
        string $encoding = "UTF-8"
606
    ): string {
607
        if (function_exists("mb_convert_case")) {
608
            return mb_convert_case($text, MB_CASE_UPPER, $encoding);
609
        }
610
611
        return strtoupper(utf8_decode($text));
612
    }
613
}
614