Passed
Pull Request — master (#39)
by Alexander
01:27
created

StringHelper   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 531
Duplicated Lines 0 %

Test Coverage

Coverage 90.78%

Importance

Changes 0
Metric Value
eloc 113
dl 0
loc 531
ccs 128
cts 141
cp 0.9078
rs 3.6
c 0
b 0
f 0
wmc 60

27 Methods

Rating   Name   Duplication   Size   Complexity  
A byteLength() 0 3 1
A byteSubstr() 0 3 1
A basename() 0 13 4
A dirname() 0 8 2
A startsWith() 0 8 2
A ucwords() 0 9 1
A strtoupper() 0 3 1
A endsWith() 0 13 3
A explode() 0 21 5
B substrReplace() 0 21 8
A strlen() 0 3 1
A endsWithIgnoringCase() 0 8 2
A truncateMiddle() 0 13 2
A normalizeNumber() 0 12 3
A truncateBegin() 0 10 2
A htmlspecialchars() 0 5 4
A strtolower() 0 3 1
A startsWithIgnoringCase() 0 8 2
A substr() 0 3 1
A countWords() 0 3 1
A ucfirst() 0 6 1
A truncateWords() 0 8 2
A floatToString() 0 5 1
A truncateEnd() 0 10 2
A base64UrlDecode() 0 3 1
A base64UrlEncode() 0 3 1
A sentence() 0 14 5

How to fix   Complexity   

Complex Class

Complex classes like StringHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StringHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Strings;
6
7
use function array_slice;
8
use function htmlspecialchars;
9
use function mb_strlen;
10
use function mb_strtolower;
11
use function mb_strtoupper;
12
use function mb_substr;
13
14
/**
15
 * Provides static methods allowing you to deal with strings more efficiently.
16
 */
17
final class StringHelper
18
{
19
    /**
20
     * Returns the number of bytes in the given string.
21
     * This method ensures the string is treated as a byte array even if `mbstring.func_overload` is turned on
22
     * by using {@see mb_strlen()}.
23
     * @param string|null $input The string being measured for length.
24
     * @return int The number of bytes in the given string.
25
     */
26 40
    public static function byteLength(?string $input): int
27
    {
28 40
        return mb_strlen((string)$input, '8bit');
29
    }
30
31
    /**
32
     * Returns the portion of string specified by the start and length parameters.
33
     * This method ensures the string is treated as a byte array by using `mb_substr()`.
34
     * @param string $input The input string. Must be one character or longer.
35
     * @param int $start The starting position.
36
     * @param int|null $length The desired portion length. If not specified or `null`, there will be
37
     * no limit on length i.e. the output will be until the end of the string.
38
     * @return string The extracted part of string, or FALSE on failure or an empty string.
39
     * @see http://www.php.net/manual/en/function.substr.php
40
     */
41 1
    public static function byteSubstr(string $input, int $start, int $length = null): string
42
    {
43 1
        return mb_substr($input, $start, $length ?? mb_strlen($input, '8bit'), '8bit');
44
    }
45
46
    /**
47
     * Returns the trailing name component of a path.
48
     * This method is similar to the php function `basename()` except that it will
49
     * treat both \ and / as directory separators, independent of the operating system.
50
     * This method was mainly created to work on php namespaces. When working with real
51
     * file paths, PHP's `basename()` should work fine for you.
52
     * Note: this method is not aware of the actual filesystem, or path components such as "..".
53
     *
54
     * @param string $path A path string.
55
     * @param string $suffix If the name component ends in suffix this will also be cut off.
56
     * @return string The trailing name component of the given path.
57
     * @see http://www.php.net/manual/en/function.basename.php
58
     */
59 1
    public static function basename(string $path, string $suffix = ''): string
60
    {
61 1
        $length = mb_strlen($suffix);
62 1
        if ($length > 0 && mb_substr($path, -$length) === $suffix) {
63 1
            $path = mb_substr($path, 0, -$length);
64
        }
65 1
        $path = rtrim(str_replace('\\', '/', $path), '/\\');
66 1
        $position = mb_strrpos($path, '/');
67 1
        if ($position !== false) {
68 1
            return mb_substr($path, $position + 1);
69
        }
70
71 1
        return $path;
72
    }
73
74
    /**
75
     * Returns parent directory's path.
76
     * This method is similar to `dirname()` except that it will treat
77
     * both \ and / as directory separators, independent of the operating system.
78
     *
79
     * @param string $path A path string.
80
     * @return string The parent directory's path.
81
     * @see http://www.php.net/manual/en/function.basename.php
82
     */
83 1
    public static function dirname(string $path): string
84
    {
85 1
        $position = mb_strrpos(str_replace('\\', '/', $path), '/');
86 1
        if ($position !== false) {
87 1
            return mb_substr($path, 0, $position);
88
        }
89
90 1
        return '';
91
    }
92
93
    /**
94
     * Truncates a string from the end to the number of characters specified.
95
     *
96
     * @param string $input The string to truncate.
97
     * @param int $length Maximum length of the truncated string including trim marker.
98
     * @param string $trimMarker String to append to the end of truncated string.
99
     * @param string $encoding The encoding to use, defaults to "UTF-8".
100
     * @return string The truncated string.
101
     */
102 1
    public static function truncateEnd(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
103
    {
104 1
        $inputLength = mb_strlen($input, $encoding);
105
106 1
        if ($inputLength <= $length) {
107 1
            return $input;
108
        }
109
110 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
111 1
        return rtrim(static::substr($input, 0, $length - $trimMarkerLength, $encoding)) . $trimMarker;
112
    }
113
114
    /**
115
     * Truncates a string to the number of words specified.
116
     *
117
     * @param string $input The string to truncate.
118
     * @param int $count How many words from original string to include into truncated string.
119
     * @param string $trimMarker String to append to the end of truncated string.
120
     * @return string The truncated string.
121
     */
122 1
    public static function truncateWords(string $input, int $count, string $trimMarker = '…'): string
123
    {
124 1
        $words = preg_split('/(\s+)/u', trim($input), -1, PREG_SPLIT_DELIM_CAPTURE);
125 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

125
        if (count(/** @scrutinizer ignore-type */ $words) / 2 > $count) {
Loading history...
126 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

126
            return implode('', array_slice(/** @scrutinizer ignore-type */ $words, 0, ($count * 2) - 1)) . $trimMarker;
Loading history...
127
        }
128
129 1
        return $input;
130
    }
131
132
    /**
133
     * Truncates a string from the beginning to the number of characters specified.
134
     *
135
     * @param string $input String to process.
136
     * @param int $length Maximum length of the truncated string including trim marker.
137
     * @param string $trimMarker String to append to the beginning.
138
     * @param string $encoding The encoding to use, defaults to "UTF-8".
139
     * @return string
140
     */
141 1
    public static function truncateBegin(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
142
    {
143 1
        $inputLength = mb_strlen($input, $encoding);
144
145 1
        if ($inputLength <= $length) {
146
            return $input;
147
        }
148
149 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
150 1
        return self::substrReplace($input, $trimMarker, 0, -$length + $trimMarkerLength, $encoding);
151
    }
152
153
    /**
154
     * Truncates a string in the middle. Keeping start and end.
155
     * `StringHelper::truncateMiddle('Hello world number 2', 8)` produces "Hell…r 2".
156
     *
157
     * @param string $input The string to truncate.
158
     * @param int $length Maximum length of the truncated string including trim marker.
159
     * @param string $trimMarker String to append in the middle of truncated string.
160
     * @param string $encoding The encoding to use, defaults to "UTF-8".
161
     * @return string The truncated string.
162
     */
163 2
    public static function truncateMiddle(string $input, int $length, string $trimMarker = '…', string $encoding = 'UTF-8'): string
164
    {
165 2
        $inputLength = mb_strlen($input, $encoding);
166
167 2
        if ($inputLength <= $length) {
168 1
            return $input;
169
        }
170
171 1
        $trimMarkerLength = mb_strlen($trimMarker, $encoding);
172 1
        $start = (int)ceil(($length - $trimMarkerLength) / 2);
173 1
        $end = $length - $start - $trimMarkerLength;
174
175 1
        return self::substrReplace($input, $trimMarker, $start, -$end, $encoding);
176
    }
177
178
    /**
179
     * Check if given string starts with specified substring.
180
     * Binary and multibyte safe.
181
     *
182
     * @param string $input Input string.
183
     * @param string|null $with Part to search inside the $string.
184
     * @return bool Returns true if first input starts with second input, false otherwise.
185
     */
186 19
    public static function startsWith(string $input, ?string $with): bool
187
    {
188 19
        $bytes = static::byteLength($with);
189 19
        if ($bytes === 0) {
190 3
            return true;
191
        }
192
193 16
        return strncmp($input, $with, $bytes) === 0;
194
    }
195
196
    /**
197
     * Check if given string starts with specified substring ignoring case.
198
     * Binary and multibyte safe.
199
     *
200
     * @param string $input Input string.
201
     * @param string|null $with Part to search inside the $string.
202
     * @return bool Returns true if first input starts with second input, false otherwise.
203
     */
204 1
    public static function startsWithIgnoringCase(string $input, ?string $with): bool
205
    {
206 1
        $bytes = static::byteLength($with);
207 1
        if ($bytes === 0) {
208 1
            return true;
209
        }
210
211 1
        return static::strtolower(static::substr($input, 0, $bytes, '8bit')) === static::strtolower($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::strtolower() 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

211
        return static::strtolower(static::substr($input, 0, $bytes, '8bit')) === static::strtolower(/** @scrutinizer ignore-type */ $with);
Loading history...
212
    }
213
214
    /**
215
     * Check if given string ends with specified substring.
216
     * Binary and multibyte safe.
217
     *
218
     * @param string $input Input string to check.
219
     * @param string|null $with Part to search inside of the $string.
220
     * @return bool Returns true if first input ends with second input, false otherwise.
221
     */
222 19
    public static function endsWith(string $input, ?string $with): bool
223
    {
224 19
        $bytes = static::byteLength($with);
225 19
        if ($bytes === 0) {
226 3
            return true;
227
        }
228
229
        // Warning check, see http://php.net/manual/en/function.substr-compare.php#refsect1-function.substr-compare-returnvalues
230 16
        if (static::byteLength($input) < $bytes) {
231 3
            return false;
232
        }
233
234 13
        return substr_compare($input, $with, -$bytes, $bytes) === 0;
235
    }
236
237
    /**
238
     * Check if given string ends with specified substring.
239
     * Binary and multibyte safe.
240
     *
241
     * @param string $input Input string to check.
242
     * @param string|null $with Part to search inside of the $string.
243
     * @return bool Returns true if first input ends with second input, false otherwise.
244
     */
245 1
    public static function endsWithIgnoringCase(string $input, ?string $with): bool
246
    {
247 1
        $bytes = static::byteLength($with);
248 1
        if ($bytes === 0) {
249 1
            return true;
250
        }
251
252 1
        return static::strtolower(mb_substr($input, -$bytes, mb_strlen($input, '8bit'), '8bit')) === static::strtolower($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::strtolower() 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

252
        return static::strtolower(mb_substr($input, -$bytes, mb_strlen($input, '8bit'), '8bit')) === static::strtolower(/** @scrutinizer ignore-type */ $with);
Loading history...
253
    }
254
255
    /**
256
     * Explodes string into array, optionally trims values and skips empty ones.
257
     *
258
     * @param string $input String to be exploded.
259
     * @param string $delimiter Delimiter. Default is ','.
260
     * @param mixed $trim Whether to trim each element. Can be:
261
     *   - boolean - to trim normally;
262
     *   - string - custom characters to trim. Will be passed as a second argument to `trim()` function.
263
     *   - callable - will be called for each value instead of trim. Takes the only argument - value.
264
     * @param bool $skipEmpty Whether to skip empty strings between delimiters. Default is false.
265
     * @return array
266
     */
267 1
    public static function explode(string $input, string $delimiter = ',', $trim = true, bool $skipEmpty = false): array
268
    {
269 1
        $result = explode($delimiter, $input);
270 1
        if ($trim !== false) {
271 1
            if ($trim === true) {
272 1
                $trim = 'trim';
273 1
            } elseif (!\is_callable($trim)) {
274 1
                $trim = static function ($v) use ($trim) {
275 1
                    return trim($v, $trim);
276 1
                };
277
            }
278 1
            $result = array_map($trim, $result);
279
        }
280 1
        if ($skipEmpty) {
281
            // Wrapped with array_values to make array keys sequential after empty values removing
282 1
            $result = array_values(array_filter($result, static function ($value) {
283 1
                return $value !== '';
284 1
            }));
285
        }
286
287 1
        return $result;
288
    }
289
290
    /**
291
     * Counts words in a string.
292
     *
293
     * @param string $input
294
     * @return int
295
     */
296 1
    public static function countWords(string $input): int
297
    {
298 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

298
        return count(/** @scrutinizer ignore-type */ preg_split('/\s+/u', $input, -1, PREG_SPLIT_NO_EMPTY));
Loading history...
299
    }
300
301
    /**
302
     * Returns string representation of number value with replaced commas to dots, if decimal point
303
     * of current locale is comma.
304
     * @param int|float|string $value
305
     * @return string
306
     */
307
    public static function normalizeNumber($value): string
308
    {
309
        $value = (string)$value;
310
311
        $localeInfo = localeconv();
312
        $decimalSeparator = $localeInfo['decimal_point'] ?? null;
313
314
        if ($decimalSeparator !== null && $decimalSeparator !== '.') {
315
            $value = str_replace($decimalSeparator, '.', $value);
316
        }
317
318
        return $value;
319
    }
320
321
    /**
322
     * Encodes string into "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648).
323
     *
324
     * > Note: Base 64 padding `=` may be at the end of the returned string.
325
     * > `=` is not transparent to URL encoding.
326
     *
327
     * @see https://tools.ietf.org/html/rfc4648#page-7
328
     * @param string $input The string to encode.
329
     * @return string Encoded string.
330
     */
331 4
    public static function base64UrlEncode(string $input): string
332
    {
333 4
        return strtr(base64_encode($input), '+/', '-_');
334
    }
335
336
    /**
337
     * Decodes "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648).
338
     *
339
     * @see https://tools.ietf.org/html/rfc4648#page-7
340
     * @param string $input Encoded string.
341
     * @return string Decoded string.
342
     */
343 4
    public static function base64UrlDecode(string $input): string
344
    {
345 4
        return base64_decode(strtr($input, '-_', '+/'));
346
    }
347
348
    /**
349
     * Safely casts a float to string independent of the current locale.
350
     *
351
     * The decimal separator will always be `.`.
352
     * @param float|int $number A floating point number or integer.
353
     * @return string The string representation of the number.
354
     */
355 1
    public static function floatToString($number): string
356
    {
357
        // . and , are the only decimal separators known in ICU data,
358
        // so its safe to call str_replace here
359 1
        return str_replace(',', '.', (string) $number);
360
    }
361
362
    /**
363
     * This method provides a unicode-safe implementation of built-in PHP function `ucfirst()`.
364
     *
365
     * @param string $string The string to be processed.
366
     * @param string $encoding The encoding to use, defaults to "UTF-8".
367
     * @return string
368
     * @see https://php.net/manual/en/function.ucfirst.php
369
     */
370 23
    public static function ucfirst(string $string, string $encoding = 'UTF-8'): string
371
    {
372 23
        $firstChar = static::substr($string, 0, 1, $encoding);
373 23
        $rest = static::substr($string, 1, null, $encoding);
374
375 23
        return static::strtoupper($firstChar, $encoding) . $rest;
376
    }
377
378
    /**
379
     * This method provides a unicode-safe implementation of built-in PHP function `ucwords()`.
380
     *
381
     * @param string $string The string to be processed.
382
     * @param string $encoding The encoding to use, defaults to "UTF-8".
383
     * @see https://php.net/manual/en/function.ucwords.php
384
     * @return string
385
     */
386 19
    public static function ucwords(string $string, string $encoding = 'UTF-8'): string
387
    {
388 19
        $words = preg_split("/\s/u", $string, -1, PREG_SPLIT_NO_EMPTY);
389
390 19
        $ucfirst = array_map(static function ($word) use ($encoding) {
391 18
            return static::ucfirst($word, $encoding);
392 19
        }, $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

392
        }, /** @scrutinizer ignore-type */ $words);
Loading history...
393
394 19
        return implode(' ', $ucfirst);
395
    }
396
397
    /**
398
     * Get string length.
399
     *
400
     * @param string $string String to calculate length for.
401
     * @param string $encoding The encoding to use, defaults to "UTF-8".
402
     * @see https://php.net/manual/en/function.mb-strlen.php
403
     * @return int
404
     */
405 1
    public static function strlen(string $string, string $encoding = 'UTF-8'): int
406
    {
407 1
        return mb_strlen($string, $encoding);
408
    }
409
410
    /**
411
     * Get part of string.
412
     *
413
     * @param string $string To get substring from.
414
     * @param int $start Character to start at.
415
     * @param int|null $length Number of characters to get.
416
     * @param string $encoding The encoding to use, defaults to "UTF-8".
417
     * @see https://php.net/manual/en/function.mb-substr.php
418
     * @return string
419
     */
420 25
    public static function substr(string $string, int $start, int $length = null, string $encoding = 'UTF-8'): string
421
    {
422 25
        return mb_substr($string, $start, $length, $encoding);
423
    }
424
425
    /**
426
     * Make a string lowercase.
427
     *
428
     * @param string $string String to process.
429
     * @param string $encoding The encoding to use, defaults to "UTF-8".
430
     * @see https://php.net/manual/en/function.mb-strtolower.php
431
     * @return string
432
     */
433 3
    public static function strtolower(string $string, string $encoding = 'UTF-8'): string
434
    {
435 3
        return mb_strtolower($string, $encoding);
436
    }
437
438
    /**
439
     * Make a string uppercase.
440
     *
441
     * @param string $string String to process.
442
     * @param string $encoding The encoding to use, defaults to "UTF-8".
443
     * @see https://php.net/manual/en/function.mb-strtoupper.php
444
     * @return string
445
     */
446 24
    public static function strtoupper(string $string, string $encoding = 'UTF-8'): string
447
    {
448 24
        return mb_strtoupper($string, $encoding);
449
    }
450
451
    /**
452
     * Replace text within a portion of a string.
453
     *
454
     * @param string $string The input string.
455
     * @param string $replacement The replacement string.
456
     * @param int $start Position to begin replacing substring at.
457
     * If start is non-negative, the replacing will begin at the start'th offset into string.
458
     * If start is negative, the replacing will begin at the start'th character from the end of string.
459
     * @param int|null $length Length of the substring to be replaced.
460
     * If given and is positive, it represents the length of the portion of string which is to be replaced.
461
     * If it is negative, it represents the number of characters from the end of string at which to stop replacing.
462
     * 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.
463
     * If length is zero then this function will have the effect of inserting replacement into string at the given start offset.
464
     * @param string $encoding The encoding to use, defaults to "UTF-8".
465
     * @return string
466
     */
467 2
    public static function substrReplace(string $string, string $replacement, int $start, ?int $length = null, string $encoding = 'UTF-8'): string
468
    {
469 2
        $stringLength = mb_strlen($string, $encoding);
470
471 2
        if ($start < 0) {
472
            $start = \max(0, $stringLength + $start);
473 2
        } elseif ($start > $stringLength) {
474
            $start = $stringLength;
475
        }
476
477 2
        if ($length !== null && $length < 0) {
478 2
            $length = \max(0, $stringLength - $start + $length);
479
        } elseif ($length === null || $length > $stringLength) {
480
            $length = $stringLength;
481
        }
482
483 2
        if (($start + $length) > $stringLength) {
484
            $length = $stringLength - $start;
485
        }
486
487 2
        return mb_substr($string, 0, $start, $encoding) . $replacement . mb_substr($string, $start + $length, $stringLength - $start - $length, $encoding);
488
    }
489
490
    /**
491
     * Convert special characters to HTML entities.
492
     *
493
     * @param string $string String to process.
494
     * @param int $flags A bitmask of one or more flags.
495
     * @param bool $doubleEncode If set to false, method will not encode existing HTML entities.
496
     * @param string|null $encoding The encoding to use, defaults to `ini_get('default_charset')`.
497
     * @return string
498
     * @see https://php.net/manual/en/function.htmlspecialchars.php
499
     */
500 1
    public static function htmlspecialchars(string $string, int $flags, bool $doubleEncode = true, string $encoding = null): string
501
    {
502 1
        return $encoding === null && $doubleEncode
503 1
            ? htmlspecialchars($string, $flags)
504 1
            : htmlspecialchars($string, $flags, $encoding ?: ini_get('default_charset'), $doubleEncode);
505
    }
506
507
    /**
508
     * Converts a list of words into a sentence.
509
     *
510
     * Special treatment is done for the last few words. For example,
511
     *
512
     * ```php
513
     * $words = ['Spain', 'France'];
514
     * echo Inflector::sentence($words);
515
     * // output: Spain and France
516
     *
517
     * $words = ['Spain', 'France', 'Italy'];
518
     * echo Inflector::sentence($words);
519
     * // output: Spain, France and Italy
520
     *
521
     * $words = ['Spain', 'France', 'Italy'];
522
     * echo Inflector::sentence($words, ' & ');
523
     * // output: Spain, France & Italy
524
     * ```
525
     *
526
     * @param array $words The words to be converted into an string.
527
     * @param string $twoWordsConnector The string connecting words when there are only two. Default to " and ".
528
     * @param string|null $lastWordConnector The string connecting the last two words. If this is null, it will
529
     * take the value of `$twoWordsConnector`.
530
     * @param string $connector The string connecting words other than those connected by
531
     * $lastWordConnector and $twoWordsConnector.
532
     * @return string The generated sentence.
533
     */
534 1
    public static function sentence(array $words, string $twoWordsConnector = ' and ', ?string $lastWordConnector = null, string $connector = ', '): ?string
535
    {
536 1
        if ($lastWordConnector === null) {
537 1
            $lastWordConnector = $twoWordsConnector;
538
        }
539 1
        switch (count($words)) {
540 1
            case 0:
541 1
                return '';
542 1
            case 1:
543 1
                return reset($words);
544 1
            case 2:
545 1
                return implode($twoWordsConnector, $words);
546
            default:
547 1
                return implode($connector, \array_slice($words, 0, -1)) . $lastWordConnector . end($words);
548
        }
549
    }
550
}
551