Passed
Push — master ( 35f020...ae8776 )
by Alexander
02:25 queued 48s
created

StringHelper::uppercase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Strings;
6
7
use function array_slice;
8
use function mb_strlen;
9
use function mb_strtolower;
10
use function mb_strtoupper;
11
use function mb_substr;
12
13
/**
14
 * Provides static methods to work with strings.
15
 */
16
final class StringHelper
17
{
18
    /**
19
     * Returns the number of bytes in the given string.
20
     * This method ensures the string is treated as a byte array even if `mbstring.func_overload` is turned on
21
     * by using {@see mb_strlen()}.
22
     * @param string|null $input The string being measured for length.
23
     * @return int The number of bytes in the given string.
24
     */
25 2
    public static function byteLength(?string $input): int
26
    {
27 2
        return mb_strlen((string)$input, '8bit');
28
    }
29
30
    /**
31
     * Returns the portion of string specified by the start and length parameters.
32
     * This method ensures the string is treated as a byte array by using `mb_substr()`.
33
     * @param string $input The input string. Must be one character or longer.
34
     * @param int $start The starting position.
35
     * @param int|null $length The desired portion length. If not specified or `null`, there will be
36
     * no limit on length i.e. the output will be until the end of the string.
37
     * @return string The extracted part of string, or FALSE on failure or an empty string.
38
     * @see http://www.php.net/manual/en/function.substr.php
39
     */
40 1
    public static function byteSubstring(string $input, int $start, int $length = null): string
41
    {
42 1
        return mb_substr($input, $start, $length ?? mb_strlen($input, '8bit'), '8bit');
43
    }
44
45
    /**
46
     * Returns the trailing name component of a path.
47
     * This method is similar to the php function `basename()` except that it will
48
     * treat both \ and / as directory separators, independent of the operating system.
49
     * This method was mainly created to work on php namespaces. When working with real
50
     * file paths, PHP's `basename()` should work fine for you.
51
     * Note: this method is not aware of the actual filesystem, or path components such as "..".
52
     *
53
     * @param string $path A path string.
54
     * @param string $suffix If the name component ends in suffix this will also be cut off.
55
     * @return string The trailing name component of the given path.
56
     * @see http://www.php.net/manual/en/function.basename.php
57
     */
58 1
    public static function baseName(string $path, string $suffix = ''): string
59
    {
60 1
        $length = mb_strlen($suffix);
61 1
        if ($length > 0 && mb_substr($path, -$length) === $suffix) {
62 1
            $path = mb_substr($path, 0, -$length);
63
        }
64 1
        $path = rtrim(str_replace('\\', '/', $path), '/\\');
65 1
        $position = mb_strrpos($path, '/');
66 1
        if ($position !== false) {
67 1
            return mb_substr($path, $position + 1);
68
        }
69
70 1
        return $path;
71
    }
72
73
    /**
74
     * Returns parent directory's path.
75
     * This method is similar to `dirname()` except that it will treat
76
     * both \ and / as directory separators, independent of the operating system.
77
     *
78
     * @param string $path A path string.
79
     * @return string The parent directory's path.
80
     * @see http://www.php.net/manual/en/function.basename.php
81
     */
82 1
    public static function directoryName(string $path): string
83
    {
84 1
        $position = mb_strrpos(str_replace('\\', '/', $path), '/');
85 1
        if ($position !== false) {
86 1
            return mb_substr($path, 0, $position);
87
        }
88
89 1
        return '';
90
    }
91
92
    /**
93
     * Get part of string.
94
     *
95
     * @param string $string To get substring from.
96
     * @param int $start Character to start at.
97
     * @param int|null $length Number of characters to get.
98
     * @param string $encoding The encoding to use, defaults to "UTF-8".
99
     * @see https://php.net/manual/en/function.mb-substr.php
100
     * @return string
101
     */
102 15
    public static function substring(string $string, int $start, int $length = null, string $encoding = 'UTF-8'): string
103
    {
104 15
        return mb_substr($string, $start, $length, $encoding);
105
    }
106
107
    /**
108
     * Replace text within a portion of a string.
109
     *
110
     * @param string $string The input string.
111
     * @param string $replacement The replacement string.
112
     * @param int $start Position to begin replacing substring at.
113
     * If start is non-negative, the replacing will begin at the start'th offset into string.
114
     * If start is negative, the replacing will begin at the start'th character from the end of string.
115
     * @param int|null $length Length of the substring to be replaced.
116
     * If given and is positive, it represents the length of the portion of string which is to be replaced.
117
     * If it is negative, it represents the number of characters from the end of string at which to stop replacing.
118
     * If it is not given, then it will default to the length of the string; i.e. end the replacing at the end of string.
119
     * If length is zero then this function will have the effect of inserting replacement into string at the given start offset.
120
     * @param string $encoding The encoding to use, defaults to "UTF-8".
121
     * @return string
122
     */
123 8
    public static function replaceSubstring(string $string, string $replacement, int $start, ?int $length = null, string $encoding = 'UTF-8'): string
124
    {
125 8
        $stringLength = mb_strlen($string, $encoding);
126
127 8
        if ($start < 0) {
128 2
            $start = \max(0, $stringLength + $start);
129 6
        } elseif ($start > $stringLength) {
130
            $start = $stringLength;
131
        }
132
133 8
        if ($length !== null && $length < 0) {
134 3
            $length = \max(0, $stringLength - $start + $length);
135 5
        } elseif ($length === null || $length > $stringLength) {
136 4
            $length = $stringLength;
137
        }
138
139 8
        if (($start + $length) > $stringLength) {
140 3
            $length = $stringLength - $start;
141
        }
142
143 8
        return mb_substr($string, 0, $start, $encoding) . $replacement . mb_substr($string, $start + $length, $stringLength - $start - $length, $encoding);
144
    }
145
146
    /**
147
     * Check if given string starts with specified substring.
148
     * Binary and multibyte safe.
149
     *
150
     * @param string $input Input string.
151
     * @param string|null $with Part to search inside the $string.
152
     * @return bool Returns true if first input starts with second input, false otherwise.
153
     */
154 19
    public static function startsWith(string $input, ?string $with): bool
155
    {
156 19
        if ($with === null) {
157 1
            return true;
158
        }
159
160 18
        if (function_exists('\str_starts_with')) {
161 18
            return \str_starts_with($input, $with);
162
        }
163
164
        $bytes = static::byteLength($with);
165
        if ($bytes === 0) {
166
            return true;
167
        }
168
169
        return strncmp($input, $with, $bytes) === 0;
170
    }
171
172
    /**
173
     * Check if given string starts with specified substring ignoring case.
174
     * Binary and multibyte safe.
175
     *
176
     * @param string $input Input string.
177
     * @param string|null $with Part to search inside the $string.
178
     * @return bool Returns true if first input starts with second input, false otherwise.
179
     */
180 1
    public static function startsWithIgnoringCase(string $input, ?string $with): bool
181
    {
182 1
        $bytes = static::byteLength($with);
183 1
        if ($bytes === 0) {
184 1
            return true;
185
        }
186
187 1
        return static::lowercase(static::substring($input, 0, $bytes, '8bit')) === static::lowercase($with);
0 ignored issues
show
Bug introduced by
It seems like $with can also be of type null; however, parameter $string of Yiisoft\Strings\StringHelper::lowercase() 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

187
        return static::lowercase(static::substring($input, 0, $bytes, '8bit')) === static::lowercase(/** @scrutinizer ignore-type */ $with);
Loading history...
188
    }
189
190
    /**
191
     * Check if given string ends with specified substring.
192
     * Binary and multibyte safe.
193
     *
194
     * @param string $input Input string to check.
195
     * @param string|null $with Part to search inside of the $string.
196
     * @return bool Returns true if first input ends with second input, false otherwise.
197
     */
198 19
    public static function endsWith(string $input, ?string $with): bool
199
    {
200 19
        if ($with === null) {
201 1
            return true;
202
        }
203
204 18
        if (function_exists('\str_ends_with')) {
205 18
            return \str_ends_with($input, $with);
206
        }
207
208
        $bytes = static::byteLength($with);
209
        if ($bytes === 0) {
210
            return true;
211
        }
212
213
        // Warning check, see http://php.net/manual/en/function.substr-compare.php#refsect1-function.substr-compare-returnvalues
214
        if (static::byteLength($input) < $bytes) {
215
            return false;
216
        }
217
218
        return substr_compare($input, $with, -$bytes, $bytes) === 0;
219
    }
220
221
    /**
222
     * Check if given string ends with specified substring.
223
     * Binary and multibyte safe.
224
     *
225
     * @param string $input Input string to check.
226
     * @param string|null $with Part to search inside of the $string.
227
     * @return bool Returns true if first input ends with second input, false otherwise.
228
     */
229 1
    public static function endsWithIgnoringCase(string $input, ?string $with): bool
230
    {
231 1
        $bytes = static::byteLength($with);
232 1
        if ($bytes === 0) {
233 1
            return true;
234
        }
235
236 1
        return static::lowercase(mb_substr($input, -$bytes, mb_strlen($input, '8bit'), '8bit')) === static::lowercase($with);
0 ignored issues
show
Bug introduced by
It seems like $with can also be of type null; however, parameter $string of Yiisoft\Strings\StringHelper::lowercase() 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

236
        return static::lowercase(mb_substr($input, -$bytes, mb_strlen($input, '8bit'), '8bit')) === static::lowercase(/** @scrutinizer ignore-type */ $with);
Loading history...
237
    }
238
239
    /**
240
     * Truncates a string from the beginning to the number of characters specified.
241
     *
242
     * @param string $input String to process.
243
     * @param int $length Maximum length of the truncated string including trim marker.
244
     * @param string $trimMarker String to append to the beginning.
245
     * @param string $encoding The encoding to use, defaults to "UTF-8".
246
     * @return string
247
     */
248 1
    public static function truncateBegin(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
249
    {
250 1
        $inputLength = mb_strlen($input, $encoding);
251
252 1
        if ($inputLength <= $length) {
253 1
            return $input;
254
        }
255
256 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
257 1
        return self::replaceSubstring($input, $trimMarker, 0, -$length + $trimMarkerLength, $encoding);
258
    }
259
260
    /**
261
     * Truncates a string in the middle. Keeping start and end.
262
     * `StringHelper::truncateMiddle('Hello world number 2', 8)` produces "Hell…r 2".
263
     *
264
     * @param string $input The string to truncate.
265
     * @param int $length Maximum length of the truncated string including trim marker.
266
     * @param string $trimMarker String to append in the middle of truncated string.
267
     * @param string $encoding The encoding to use, defaults to "UTF-8".
268
     * @return string The truncated string.
269
     */
270 2
    public static function truncateMiddle(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
271
    {
272 2
        $inputLength = mb_strlen($input, $encoding);
273
274 2
        if ($inputLength <= $length) {
275 1
            return $input;
276
        }
277
278 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
279 1
        $start = (int)ceil(($length - $trimMarkerLength) / 2);
280 1
        $end = $length - $start - $trimMarkerLength;
281
282 1
        return self::replaceSubstring($input, $trimMarker, $start, -$end, $encoding);
283
    }
284
285
    /**
286
     * Truncates a string from the end to the number of characters specified.
287
     *
288
     * @param string $input The string to truncate.
289
     * @param int $length Maximum length of the truncated string including trim marker.
290
     * @param string $trimMarker String to append to the end of truncated string.
291
     * @param string $encoding The encoding to use, defaults to "UTF-8".
292
     * @return string The truncated string.
293
     */
294 1
    public static function truncateEnd(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
295
    {
296 1
        $inputLength = mb_strlen($input, $encoding);
297
298 1
        if ($inputLength <= $length) {
299 1
            return $input;
300
        }
301
302 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
303 1
        return rtrim(mb_substr($input, 0, $length - $trimMarkerLength, $encoding)) . $trimMarker;
304
    }
305
306
    /**
307
     * Truncates a string to the number of words specified.
308
     *
309
     * @param string $input The string to truncate.
310
     * @param int $count How many words from original string to include into truncated string.
311
     * @param string $trimMarker String to append to the end of truncated string.
312
     * @return string The truncated string.
313
     */
314 1
    public static function truncateWords(string $input, int $count, string $trimMarker = '…'): string
315
    {
316 1
        $words = preg_split('/(\s+)/u', trim($input), -1, PREG_SPLIT_DELIM_CAPTURE);
317 1
        if (count($words) / 2 > $count) {
0 ignored issues
show
Bug introduced by
It seems like $words can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, 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

317
        if (count(/** @scrutinizer ignore-type */ $words) / 2 > $count) {
Loading history...
318 1
            return implode('', array_slice($words, 0, ($count * 2) - 1)) . $trimMarker;
0 ignored issues
show
Bug introduced by
It seems like $words can also be of type false; however, parameter $array of array_slice() does only seem to accept array, 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

318
            return implode('', array_slice(/** @scrutinizer ignore-type */ $words, 0, ($count * 2) - 1)) . $trimMarker;
Loading history...
319
        }
320
321 1
        return $input;
322
    }
323
324
    /**
325
     * Get string length.
326
     *
327
     * @param string $string String to calculate length for.
328
     * @param string $encoding The encoding to use, defaults to "UTF-8".
329
     * @see https://php.net/manual/en/function.mb-strlen.php
330
     * @return int
331
     */
332 1
    public static function length(string $string, string $encoding = 'UTF-8'): int
333
    {
334 1
        return mb_strlen($string, $encoding);
335
    }
336
337
    /**
338
     * Counts words in a string.
339
     *
340
     * @param string $input
341
     * @return int
342
     */
343 1
    public static function countWords(string $input): int
344
    {
345 1
        return count(preg_split('/\s+/u', $input, -1, PREG_SPLIT_NO_EMPTY));
0 ignored issues
show
Bug introduced by
It seems like preg_split('/\s+/u', $in...gs\PREG_SPLIT_NO_EMPTY) can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, 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

345
        return count(/** @scrutinizer ignore-type */ preg_split('/\s+/u', $input, -1, PREG_SPLIT_NO_EMPTY));
Loading history...
346
    }
347
348
    /**
349
     * Make a string lowercase.
350
     *
351
     * @param string $string String to process.
352
     * @param string $encoding The encoding to use, defaults to "UTF-8".
353
     * @see https://php.net/manual/en/function.mb-strtolower.php
354
     * @return string
355
     */
356 3
    public static function lowercase(string $string, string $encoding = 'UTF-8'): string
357
    {
358 3
        return mb_strtolower($string, $encoding);
359
    }
360
361
    /**
362
     * Make a string uppercase.
363
     *
364
     * @param string $string String to process.
365
     * @param string $encoding The encoding to use, defaults to "UTF-8".
366
     * @see https://php.net/manual/en/function.mb-strtoupper.php
367
     * @return string
368
     */
369 15
    public static function uppercase(string $string, string $encoding = 'UTF-8'): string
370
    {
371 15
        return mb_strtoupper($string, $encoding);
372
    }
373
374
    /**
375
     * Make a string's first character uppercase.
376
     *
377
     * @param string $string The string to be processed.
378
     * @param string $encoding The encoding to use, defaults to "UTF-8".
379
     * @return string
380
     * @see https://php.net/manual/en/function.ucfirst.php
381
     */
382 14
    public static function uppercaseFirstCharacter(string $string, string $encoding = 'UTF-8'): string
383
    {
384 14
        $firstCharacter = static::substring($string, 0, 1, $encoding);
385 14
        $rest = static::substring($string, 1, null, $encoding);
386
387 14
        return static::uppercase($firstCharacter, $encoding) . $rest;
388
    }
389
390
    /**
391
     * Uppercase the first character of each word in a string.
392
     *
393
     * @param string $string The string to be processed.
394
     * @param string $encoding The encoding to use, defaults to "UTF-8".
395
     * @see https://php.net/manual/en/function.ucwords.php
396
     * @return string
397
     */
398 10
    public static function uppercaseFirstCharacterInEachWord(string $string, string $encoding = 'UTF-8'): string
399
    {
400 10
        $words = preg_split("/\s/u", $string, -1, PREG_SPLIT_NO_EMPTY);
401
402 10
        $wordsWithUppercaseFirstCharacter = array_map(static function ($word) use ($encoding) {
403 9
            return static::uppercaseFirstCharacter($word, $encoding);
404 10
        }, $words);
0 ignored issues
show
Bug introduced by
It seems like $words can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, 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

404
        }, /** @scrutinizer ignore-type */ $words);
Loading history...
405
406 10
        return implode(' ', $wordsWithUppercaseFirstCharacter);
407
    }
408
409
    /**
410
     * Encodes string into "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648).
411
     *
412
     * > Note: Base 64 padding `=` may be at the end of the returned string.
413
     * > `=` is not transparent to URL encoding.
414
     *
415
     * @see https://tools.ietf.org/html/rfc4648#page-7
416
     * @param string $input The string to encode.
417
     * @return string Encoded string.
418
     */
419 4
    public static function base64UrlEncode(string $input): string
420
    {
421 4
        return strtr(base64_encode($input), '+/', '-_');
422
    }
423
424
    /**
425
     * Decodes "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648).
426
     *
427
     * @see https://tools.ietf.org/html/rfc4648#page-7
428
     * @param string $input Encoded string.
429
     * @return string Decoded string.
430
     */
431 4
    public static function base64UrlDecode(string $input): string
432
    {
433 4
        return base64_decode(strtr($input, '-_', '+/'));
434
    }
435
}
436