Passed
Push — master ( 12e656...9e9627 )
by Nikolaos
02:33
created

Str::countVowels()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
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
use function ucwords;
40
use function utf8_decode;
41
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 4
    final public static function concat(): string
91
    {
92 4
        $arguments = func_get_args();
93
94 4
        if (count($arguments) < 3) {
95 2
            throw new Exception(
96 2
                "concat needs at least three parameters"
97
            );
98
        }
99
100 2
        $delimiter = Arr::first($arguments);
101 2
        $arguments = Arr::sliceRight($arguments);
102 2
        $first     = Arr::first($arguments);
103 2
        $last      = Arr::last($arguments);
104 2
        $prefix    = "";
105 2
        $suffix    = "";
106 2
        $data      = [];
107
108 2
        if (self::startsWith($first, $delimiter)) {
109 2
            $prefix = $delimiter;
110
        }
111
112 2
        if (self::endsWith($last, $delimiter)) {
113 2
            $suffix = $delimiter;
114
        }
115
116 2
        foreach ($arguments as $argument) {
117 2
            $data[] = trim($argument, $delimiter);
118
        }
119
120 2
        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 2
    final public static function countVowels(string $text): int
132
    {
133 2
        preg_match_all("/[aeiou]/i", $text, $matches);
134
135 2
        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 2
    final public static function decapitalize(
150
        string $text,
151
        bool $upperRest = false,
152
        string $encoding = "UTF-8"
153
    ): string {
154 2
        $substr = mb_substr($text, 1);
155
156 2
        if ($upperRest) {
157 2
            $suffix = mb_strtoupper($substr, $encoding);
158
        } else {
159 2
            $suffix = $substr;
160
        }
161
162 2
        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 2
    final public static function decrement(
182
        string $text,
183
        string $separator = "_"
184
    ): string {
185 2
        $number = 0;
186 2
        $parts  = explode($separator, $text);
187
188 2
        if (isset($parts[1])) {
189 2
            $number = $parts[1];
190 2
            $number--;
191 2
            if ($number <= 0) {
192 2
                return $parts[0];
193
            }
194
        }
195
196 2
        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 56
    final public static function dirFromFile(string $file): string
208
    {
209 56
        $name  = pathinfo($file, PATHINFO_FILENAME);
210 56
        $start = substr($name, 0, -2);
211
212 56
        if (!$start) {
213 10
            $start = substr($name, 0, 1);
214
        }
215
216 56
        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 80
    final public static function dirSeparator(string $directory): string
228
    {
229 80
        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 12
    final public static function endsWith(
242
        string $haystack,
243
        string $needle,
244
        bool $ignoreCase = true
245
    ): bool {
246 12
        if ('' === $haystack) {
247 4
            return false;
248
        }
249
250 8
        return 0 === substr_compare(
251 8
            $haystack,
252 8
            $needle,
253 8
            -strlen($needle),
254 8
            strlen($needle),
255 8
            $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 2
    final public static function firstBetween(
270
        string $text,
271
        string $start,
272
        string $end
273
    ): string {
274 2
        $result = mb_strstr($text, $start);
275 2
        $result = (false === $result) ? '' : $result;
276 2
        $result = mb_strstr($result, $end, true);
277 2
        $result = (false === $result) ? '' : $result;
278
279 2
        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 4
    public function friendly(
294
        string $text,
295
        string $separator = "-",
296
        bool $lowercase = true,
297
        $replace = null
298
    ): string {
299
300 4
        if (null !== $replace) {
301 4
            if (!is_array($replace) && !is_string($replace)) {
302 2
                throw new Exception(
303 2
                    "Parameter replace must be an array or a string"
304
                );
305
            }
306
307 2
            if (is_string($replace)) {
308 2
                $replace = [$replace];
309
            }
310
311 2
            $text = str_replace($replace, " ", $text);
312
        }
313
314 2
        $friendly = preg_replace(
315 2
            "/[^a-zA-Z0-9\\/_|+ -]/",
316 2
            "",
317 2
            $text
318
        );
319
320 2
        if ($lowercase) {
321 2
            $friendly = strtolower($friendly);
322
        }
323
324 2
        $friendly = preg_replace("/[\\/_|+ -]+/", $separator, $friendly);
325 2
        $friendly = trim($friendly, $separator);
326
327 2
        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 2
    final public static function humanize(string $text): string
338
    {
339 2
        $result = preg_replace('#[_-]+#', ' ', trim($text));
340
341 2
        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 48
    final public static function includes(
353
        string $haystack,
354
        string $needle
355
    ): bool {
356 48
        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 2
    final public static function increment(
369
        string $text,
370
        string $separator = "_"
371
    ): string {
372 2
        $parts  = explode($separator, $text);
373 2
        $number = 1;
374
375 2
        if (isset($parts[1])) {
376 2
            $number = ((int) $parts[1]) + 1;
377
        }
378
379 2
        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 2
    final public static function isAnagram(string $first, string $second): bool
392
    {
393 2
        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 2
    final public static function isLower(
405
        string $text,
406
        string $encoding = "UTF-8"
407
    ): bool {
408 2
        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 2
    final public static function isPalindrome(string $text): bool
419
    {
420 2
        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 2
    final public static function isUpper(
432
        string $text,
433
        string $encoding = "UTF-8"
434
    ): bool {
435 2
        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 8
    final public static function len(
447
        string $text,
448
        string $encoding = "UTF-8"
449
    ): int {
450 8
        return mb_strlen($text, $encoding);
451
    }
452
453
    /**
454
     * Lowercases a string, this function makes use of the mbstring extension if
455
     * available
456
     *
457
     * @param string $text
458
     * @param string $encoding
459
     *
460
     * @return string
461
     */
462 10
    final public static function lower(
463
        string $text,
464
        string $encoding = "UTF-8"
465
    ): string {
466 10
        return mb_convert_case($text, MB_CASE_LOWER, $encoding);
467
    }
468
469
    /**
470
     * Generates a random string based on the given type. Type is one of the
471
     * RANDOM_* constants
472
     *
473
     * @param int $type
474
     * @param int $length
475
     *
476
     * @return string
477
     */
478 12
    final public static function random(
479
        int $type = self::RANDOM_ALNUM,
480
        int $length = 8
481
    ): string {
482 12
        $text  = "";
483 12
        $type  = ($type < 0 || $type > 5) ? self::RANDOM_ALNUM : $type;
484
        $pools = [
485 12
            self::RANDOM_ALPHA    => array_merge(
486 12
                range('a', 'z'),
487 12
                range('A', 'Z')
488
            ),
489 12
            self::RANDOM_HEXDEC   => array_merge(
490 12
                range(0, 9),
491 12
                range('a', 'f')
492
            ),
493 12
            self::RANDOM_NUMERIC  => range(0, 9),
494 12
            self::RANDOM_NOZERO   => range(1, 9),
495 12
            self::RANDOM_DISTINCT => str_split('2345679ACDEFHJKLMNPRSTUVWXYZ'),
496 12
            self::RANDOM_ALNUM    => array_merge(
497 12
                range(0, 9),
498 12
                range('a', 'z'),
499 12
                range('A', 'Z')
500
            ),
501
        ];
502
503 12
        $end = count($pools[$type]) - 1;
504
505 12
        while (strlen($text) < $length) {
506 12
            $text .= $pools[$type][mt_rand(0, $end)];
507
        }
508
509 12
        return $text;
510
    }
511
512
    /**
513
     * Reduces multiple slashes in a string to single slashes
514
     *
515
     * @param string $text
516
     *
517
     * @return string
518
     */
519 2
    final public static function reduceSlashes(string $text): string
520
    {
521 2
        $result = preg_replace('#(?<!:)//+#', '/', $text);
522
523 2
        return (null === $result) ? '' : $result;
524
    }
525
526
    /**
527
     * Check if a string starts with a given string
528
     *
529
     * @param string $haystack
530
     * @param string $needle
531
     * @param bool   $ignoreCase
532
     *
533
     * @return bool
534
     */
535 138
    final public static function startsWith(
536
        string $haystack,
537
        string $needle,
538
        bool $ignoreCase = true
539
    ): bool {
540 138
        if ('' === $haystack) {
541 4
            return false;
542
        }
543
544 134
        return 0 === substr_compare(
545 134
            $haystack,
546 134
            $needle,
547 134
            0,
548 134
            strlen($needle),
549 134
            $ignoreCase
550
        );
551
    }
552
553
    /**
554
     * Makes a phrase underscored instead of spaced
555
     *
556
     * @param string $text
557
     * @param string $encoding
558
     *
559
     * @return string
560
     */
561 2
    final public static function ucwords(
562
        string $text,
563
        string $encoding = "UTF-8"
564
    ): string {
565 2
        return mb_convert_case($text, MB_CASE_TITLE, $encoding);
566
    }
567
568
    /**
569
     * Makes a phrase underscored instead of spaced
570
     *
571
     * @param string $text
572
     *
573
     * @return string
574
     */
575 2
    final public static function underscore(string $text): string
576
    {
577 2
        $result = preg_replace('#\s+#', '_', trim($text));
578
579 2
        return (null === $result) ? '' : $result;
580
    }
581
582
    /**
583
     * Uppercases a string, this function makes use of the mbstring extension if
584
     * available
585
     *
586
     * @param string $text
587
     * @param string $encoding
588
     *
589
     * @return string
590
     */
591 10
    final public static function upper(
592
        string $text,
593
        string $encoding = "UTF-8"
594
    ): string {
595 10
        return mb_convert_case($text, MB_CASE_UPPER, $encoding);
596
    }
597
}
598