Passed
Push — master ( 68a02e...9d4783 )
by Alexander
02:32 queued 01:08
created

Inflector::sentence()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 11
c 1
b 0
f 0
nc 8
nop 4
dl 0
loc 14
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
1
<?php
2
3
namespace Yiisoft\Strings;
4
5
/**
6
 * Inflector provides concrete implementation for [[Inflector]].
7
 */
8
final class Inflector
9
{
10
    /**
11
     * @var array the rules for converting a word into its plural form.
12
     * The keys are the regular expressions and the values are the corresponding replacements.
13
     */
14
    private $pluralizeRules = [
15
        '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1',
16
        '/^(sea[- ]bass)$/i' => '\1',
17
        '/(m)ove$/i' => '\1oves',
18
        '/(f)oot$/i' => '\1eet',
19
        '/(h)uman$/i' => '\1umans',
20
        '/(s)tatus$/i' => '\1tatuses',
21
        '/(s)taff$/i' => '\1taff',
22
        '/(t)ooth$/i' => '\1eeth',
23
        '/(quiz)$/i' => '\1zes',
24
        '/^(ox)$/i' => '\1\2en',
25
        '/([m|l])ouse$/i' => '\1ice',
26
        '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
27
        '/(x|ch|ss|sh)$/i' => '\1es',
28
        '/([^aeiouy]|qu)y$/i' => '\1ies',
29
        '/(hive)$/i' => '\1s',
30
        '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
31
        '/sis$/i' => 'ses',
32
        '/([ti])um$/i' => '\1a',
33
        '/(p)erson$/i' => '\1eople',
34
        '/(m)an$/i' => '\1en',
35
        '/(c)hild$/i' => '\1hildren',
36
        '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
37
        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
38
        '/us$/i' => 'uses',
39
        '/(alias)$/i' => '\1es',
40
        '/(ax|cris|test)is$/i' => '\1es',
41
        '/(currenc)y$/' => '\1ies',
42
        '/on$/i' => 'a',
43
        '/s$/' => 's',
44
        '/^$/' => '',
45
        '/$/' => 's',
46
    ];
47
    /**
48
     * @var array the rules for converting a word into its singular form.
49
     * The keys are the regular expressions and the values are the corresponding replacements.
50
     */
51
    private $singularizeRules = [
52
        '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1',
53
        '/^(sea[- ]bass)$/i' => '\1',
54
        '/(s)tatuses$/i' => '\1tatus',
55
        '/(f)eet$/i' => '\1oot',
56
        '/(t)eeth$/i' => '\1ooth',
57
        '/^(.*)(menu)s$/i' => '\1\2',
58
        '/(quiz)zes$/i' => '\\1',
59
        '/(matr)ices$/i' => '\1ix',
60
        '/(vert|ind)ices$/i' => '\1ex',
61
        '/^(ox)en/i' => '\1',
62
        '/(alias)(es)*$/i' => '\1',
63
        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
64
        '/([ftw]ax)es/i' => '\1',
65
        '/(cris|ax|test)es$/i' => '\1is',
66
        '/(shoe|slave)s$/i' => '\1',
67
        '/(o)es$/i' => '\1',
68
        '/ouses$/i' => 'ouse',
69
        '/([^a])uses$/i' => '\1us',
70
        '/([m|l])ice$/i' => '\1ouse',
71
        '/(x|ch|ss|sh)es$/i' => '\1',
72
        '/(m)ovies$/i' => '\1\2ovie',
73
        '/(s)eries$/i' => '\1\2eries',
74
        '/([^aeiouy]|qu)ies$/i' => '\1y',
75
        '/([lr])ves$/i' => '\1f',
76
        '/(tive)s$/i' => '\1',
77
        '/(hive)s$/i' => '\1',
78
        '/(drive)s$/i' => '\1',
79
        '/([^fo])ves$/i' => '\1fe',
80
        '/(^analy)ses$/i' => '\1sis',
81
        '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
82
        '/criteria$/i' => 'criterion',
83
        '/([ti])a$/i' => '\1um',
84
        '/(p)eople$/i' => '\1\2erson',
85
        '/(m)en$/i' => '\1an',
86
        '/(c)hildren$/i' => '\1\2hild',
87
        '/(n)ews$/i' => '\1\2ews',
88
        '/(n)etherlands$/i' => '\1\2etherlands',
89
        '/eaus$/i' => 'eau',
90
        '/(currenc)ies$/i' => '\1y',
91
        '/^(.*us)$/i' => '\\1',
92
        '/s$/i' => '',
93
    ];
94
    /**
95
     * @var array the special rules for converting a word between its plural form and singular form.
96
     * The keys are the special words in singular form, and the values are the corresponding plural form.
97
     */
98
    private $specialRules = [
99
        'atlas' => 'atlases',
100
        'beef' => 'beefs',
101
        'brother' => 'brothers',
102
        'cafe' => 'cafes',
103
        'child' => 'children',
104
        'cookie' => 'cookies',
105
        'corpus' => 'corpuses',
106
        'cow' => 'cows',
107
        'curve' => 'curves',
108
        'foe' => 'foes',
109
        'ganglion' => 'ganglions',
110
        'genie' => 'genies',
111
        'genus' => 'genera',
112
        'graffito' => 'graffiti',
113
        'hoof' => 'hoofs',
114
        'loaf' => 'loaves',
115
        'man' => 'men',
116
        'money' => 'monies',
117
        'mongoose' => 'mongooses',
118
        'move' => 'moves',
119
        'mythos' => 'mythoi',
120
        'niche' => 'niches',
121
        'numen' => 'numina',
122
        'occiput' => 'occiputs',
123
        'octopus' => 'octopuses',
124
        'opus' => 'opuses',
125
        'ox' => 'oxen',
126
        'pasta' => 'pasta',
127
        'penis' => 'penises',
128
        'sex' => 'sexes',
129
        'soliloquy' => 'soliloquies',
130
        'testis' => 'testes',
131
        'trilby' => 'trilbys',
132
        'turf' => 'turfs',
133
        'wave' => 'waves',
134
        'Amoyese' => 'Amoyese',
135
        'bison' => 'bison',
136
        'Borghese' => 'Borghese',
137
        'bream' => 'bream',
138
        'breeches' => 'breeches',
139
        'britches' => 'britches',
140
        'buffalo' => 'buffalo',
141
        'cantus' => 'cantus',
142
        'carp' => 'carp',
143
        'chassis' => 'chassis',
144
        'clippers' => 'clippers',
145
        'cod' => 'cod',
146
        'coitus' => 'coitus',
147
        'Congoese' => 'Congoese',
148
        'contretemps' => 'contretemps',
149
        'corps' => 'corps',
150
        'debris' => 'debris',
151
        'diabetes' => 'diabetes',
152
        'djinn' => 'djinn',
153
        'eland' => 'eland',
154
        'elk' => 'elk',
155
        'equipment' => 'equipment',
156
        'Faroese' => 'Faroese',
157
        'flounder' => 'flounder',
158
        'Foochowese' => 'Foochowese',
159
        'gallows' => 'gallows',
160
        'Genevese' => 'Genevese',
161
        'Genoese' => 'Genoese',
162
        'Gilbertese' => 'Gilbertese',
163
        'graffiti' => 'graffiti',
164
        'headquarters' => 'headquarters',
165
        'herpes' => 'herpes',
166
        'hijinks' => 'hijinks',
167
        'Hottentotese' => 'Hottentotese',
168
        'information' => 'information',
169
        'innings' => 'innings',
170
        'jackanapes' => 'jackanapes',
171
        'Kiplingese' => 'Kiplingese',
172
        'Kongoese' => 'Kongoese',
173
        'Lucchese' => 'Lucchese',
174
        'mackerel' => 'mackerel',
175
        'Maltese' => 'Maltese',
176
        'mews' => 'mews',
177
        'moose' => 'moose',
178
        'mumps' => 'mumps',
179
        'Nankingese' => 'Nankingese',
180
        'news' => 'news',
181
        'nexus' => 'nexus',
182
        'Niasese' => 'Niasese',
183
        'Pekingese' => 'Pekingese',
184
        'Piedmontese' => 'Piedmontese',
185
        'pincers' => 'pincers',
186
        'Pistoiese' => 'Pistoiese',
187
        'pliers' => 'pliers',
188
        'Portuguese' => 'Portuguese',
189
        'proceedings' => 'proceedings',
190
        'rabies' => 'rabies',
191
        'rice' => 'rice',
192
        'rhinoceros' => 'rhinoceros',
193
        'salmon' => 'salmon',
194
        'Sarawakese' => 'Sarawakese',
195
        'scissors' => 'scissors',
196
        'series' => 'series',
197
        'Shavese' => 'Shavese',
198
        'shears' => 'shears',
199
        'siemens' => 'siemens',
200
        'species' => 'species',
201
        'swine' => 'swine',
202
        'testes' => 'testes',
203
        'trousers' => 'trousers',
204
        'trout' => 'trout',
205
        'tuna' => 'tuna',
206
        'Vermontese' => 'Vermontese',
207
        'Wenchowese' => 'Wenchowese',
208
        'whiting' => 'whiting',
209
        'wildebeest' => 'wildebeest',
210
        'Yengeese' => 'Yengeese',
211
    ];
212
    /**
213
     * @var array fallback map for transliteration used by [[transliterate()]] when intl isn't available.
214
     */
215
    private $transliterationMap = [
216
        'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C',
217
        'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
218
        'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O',
219
        'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH',
220
        'ß' => 'ss',
221
        'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c',
222
        'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
223
        'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o',
224
        'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th',
225
        'ÿ' => 'y',
226
    ];
227
    /**
228
     * Shortcut for `Any-Latin; NFKD` transliteration rule.
229
     *
230
     * The rule is strict, letters will be transliterated with
231
     * the closest sound-representation chars. The result may contain any UTF-8 chars. For example:
232
     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to
233
     * `huò qǔ dào dochira Ukraí̈nsʹka: g̀,ê, Srpska: đ, n̂, d̂! ¿Español?`.
234
     *
235
     * Used in [[transliterate()]].
236
     * For detailed information see [unicode normalization forms](http://unicode.org/reports/tr15/#Normalization_Forms_Table)
237
     * @see http://unicode.org/reports/tr15/#Normalization_Forms_Table
238
     * @see transliterate()
239
     */
240
    public const TRANSLITERATE_STRICT = 'Any-Latin; NFKD';
241
    /**
242
     * Shortcut for `Any-Latin; Latin-ASCII` transliteration rule.
243
     *
244
     * The rule is medium, letters will be
245
     * transliterated to characters of Latin-1 (ISO 8859-1) ASCII table. For example:
246
     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to
247
     * `huo qu dao dochira Ukrainsʹka: g,e, Srpska: d, n, d! ¿Espanol?`.
248
     *
249
     * Used in [[transliterate()]].
250
     * For detailed information see [unicode normalization forms](http://unicode.org/reports/tr15/#Normalization_Forms_Table)
251
     * @see http://unicode.org/reports/tr15/#Normalization_Forms_Table
252
     * @see transliterate()
253
     */
254
    public const TRANSLITERATE_MEDIUM = 'Any-Latin; Latin-ASCII';
255
    /**
256
     * Shortcut for `Any-Latin; Latin-ASCII; [\u0080-\uffff] remove` transliteration rule.
257
     *
258
     * The rule is loose,
259
     * letters will be transliterated with the characters of Basic Latin Unicode Block.
260
     * For example:
261
     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to
262
     * `huo qu dao dochira Ukrainska: g,e, Srpska: d, n, d! Espanol?`.
263
     *
264
     * Used in [[transliterate()]].
265
     * For detailed information see [unicode normalization forms](http://unicode.org/reports/tr15/#Normalization_Forms_Table)
266
     * @see http://unicode.org/reports/tr15/#Normalization_Forms_Table
267
     * @see transliterate()
268
     */
269
    public const TRANSLITERATE_LOOSE = 'Any-Latin; Latin-ASCII; [\u0080-\uffff] remove';
270
271
    /**
272
     * @var mixed Either a [[\Transliterator]], or a string from which a [[\Transliterator]] can be built
273
     * for transliteration. Used by [[transliterate()]] when intl is available. Defaults to [[TRANSLITERATE_LOOSE]]
274
     * @see https://secure.php.net/manual/en/transliterator.transliterate.php
275
     */
276
    private $transliterator = self::TRANSLITERATE_LOOSE;
277
278
    private $withoutIntl = false;
279
280
    public function withPluralizeRules(array $rules): self
281
    {
282
        $new = clone $this;
283
        $new->pluralizeRules = $rules;
284
        return $new;
285
    }
286
287
    public function getPluralizeRules(): array
288
    {
289
        return $this->pluralizeRules;
290
    }
291
292
    public function withSingularizeRules(array $rules): self
293
    {
294
        $new = clone $this;
295
        $new->singularizeRules = $rules;
296
        return $new;
297
    }
298
299
    public function getSingularizeRules(): array
300
    {
301
        return $this->singularizeRules;
302
    }
303
304
    public function withSpecialRules(array $rules): self
305
    {
306
        $new = clone $this;
307
        $new->specialRules = $rules;
308
        return $new;
309
    }
310
311
    public function getSpecialRules(): array
312
    {
313
        return $this->specialRules;
314
    }
315
316
    public function withTransliterator(string $transliterator): self
317
    {
318
        $new = clone $this;
319
        $new->transliterator = $transliterator;
320
        return $new;
321
    }
322
323
    public function withTransliterationMap(array $transliterationMap): self
324
    {
325
        $new = clone $this;
326
        $new->transliterationMap = $transliterationMap;
327
        return $new;
328
    }
329
330 18
    public function withoutIntl(): self
331
    {
332 18
        $new = clone $this;
333 18
        $new->withoutIntl = true;
334 18
        return $new;
335
    }
336
337
    /**
338
     * Converts a word to its plural form.
339
     * Note that this is for English only!
340
     * For example, 'apple' will become 'apples', and 'child' will become 'children'.
341
     * @param string $word the word to be pluralized
342
     * @return string the pluralized word
343
     */
344 2
    public function pluralize(string $word): string
345
    {
346 2
        if (isset($this->specialRules[$word])) {
347 1
            return $this->specialRules[$word];
348
        }
349 2
        foreach ($this->pluralizeRules as $rule => $replacement) {
350 2
            if (preg_match($rule, $word)) {
351 2
                return preg_replace($rule, $replacement, $word);
352
            }
353
        }
354
355
        return $word;
356
    }
357
358
    /**
359
     * Returns the singular of the $word.
360
     * @param string $word the english word to singularize
361
     * @return string Singular noun.
362
     */
363 2
    public function singularize(string $word): string
364
    {
365 2
        $result = array_search($word, $this->specialRules, true);
366 2
        if ($result !== false) {
367 1
            return $result;
368
        }
369 2
        foreach ($this->singularizeRules as $rule => $replacement) {
370 2
            if (preg_match($rule, $word)) {
371 2
                return preg_replace($rule, $replacement, $word);
372
            }
373
        }
374
375 1
        return $word;
376
    }
377
378
    /**
379
     * Converts an underscored or CamelCase word into a English
380
     * sentence.
381
     * @param string $words
382
     * @param bool $uppercaseAll whether to set all words to uppercase
383
     * @return string
384
     */
385 1
    public function titleize(string $words, bool $uppercaseAll = false): string
386
    {
387 1
        $words = $this->humanize($this->underscore($words), $uppercaseAll);
388
389 1
        return $uppercaseAll ? StringHelper::ucwords($words) : StringHelper::ucfirst($words);
390
    }
391
392
    /**
393
     * Returns given word as CamelCased.
394
     *
395
     * Converts a word like "send_email" to "SendEmail". It
396
     * will remove non alphanumeric character from the word, so
397
     * "who's online" will be converted to "WhoSOnline".
398
     * @param string $word the word to CamelCase
399
     * @return string
400
     * @see variablize()
401
     */
402 3
    public function camelize(string $word): string
403
    {
404 3
        return str_replace(' ', '', StringHelper::ucwords(preg_replace('/[^\pL\pN]+/u', ' ', $word)));
405
    }
406
407
    /**
408
     * Converts a CamelCase name into space-separated words.
409
     * For example, 'PostTag' will be converted to 'Post Tag'.
410
     * @param string $name the string to be converted
411
     * @param bool $ucwords whether to capitalize the first letter in each word
412
     * @return string the resulting words
413
     */
414 1
    public function camel2words(string $name, bool $ucwords = true): string
415
    {
416 1
        $label = mb_strtolower(trim(str_replace([
417 1
            '-',
418
            '_',
419
            '.',
420 1
        ], ' ', preg_replace('/(?<!\p{Lu})(\p{Lu})|(\p{Lu})(?=\p{Ll})/u', ' \0', $name))));
421
422 1
        return $ucwords ? StringHelper::ucwords($label) : $label;
423
    }
424
425
    /**
426
     * Converts a CamelCase name into an ID in lowercase.
427
     * Words in the ID may be concatenated using the specified character (defaults to '-').
428
     * For example, 'PostTag' will be converted to 'post-tag'.
429
     * @param string $name the string to be converted
430
     * @param string $separator the character used to concatenate the words in the ID
431
     * @param bool $strict whether to insert a separator between two consecutive uppercase chars, defaults to false
432
     * @return string the resulting ID
433
     */
434 27
    public function camel2id(string $name, string $separator = '-', bool $strict = false): string
435
    {
436 27
        $regex = $strict
437 11
            ? '/(?<=\p{L})(\p{Lu})/u'
438 27
            : '/(?<=\p{L})(?<!\p{Lu})(\p{Lu})/u';
439 27
        $result = preg_replace($regex, addslashes($separator) . '\1', $name);
440
441 27
        if ($separator !== '_') {
442 14
            $result = str_replace('_', $separator, $result);
443
        }
444
445 27
        return mb_strtolower(trim($result, $separator));
446
    }
447
448
    /**
449
     * Converts an ID into a CamelCase name.
450
     * Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name.
451
     * For example, 'post-tag' is converted to 'PostTag'.
452
     * @param string $id the ID to be converted
453
     * @param string $separator the character used to separate the words in the ID
454
     * @return string the resulting CamelCase name
455
     */
456 9
    public function id2camel(string $id, string $separator = '-'): string
457
    {
458 9
        return str_replace(' ', '', StringHelper::ucwords(str_replace($separator, ' ', $id)));
459
    }
460
461
    /**
462
     * Converts any "CamelCased" into an "underscored_word".
463
     * @param string $words the word(s) to underscore
464
     * @return string
465
     */
466 3
    public function underscore(string $words): string
467
    {
468 3
        return mb_strtolower(preg_replace('/(?<=\\pL)(\\p{Lu})/u', '_\\1', $words));
469
    }
470
471
    /**
472
     * Returns a human-readable string from $word.
473
     * @param string $word the string to humanize
474
     * @param bool $ucAll whether to set all words to uppercase or not
475
     * @return string
476
     */
477 2
    public function humanize(string $word, bool $ucAll = false): string
478
    {
479 2
        $word = str_replace('_', ' ', preg_replace('/_id$/', '', $word));
480
481 2
        return $ucAll ? StringHelper::ucwords($word) : StringHelper::ucfirst($word);
482
    }
483
484
    /**
485
     * Same as camelize but first char is in lowercase.
486
     *
487
     * Converts a word like "send_email" to "sendEmail". It
488
     * will remove non alphanumeric character from the word, so
489
     * "who's online" will be converted to "whoSOnline".
490
     * @param string $word to lowerCamelCase
491
     * @return string
492
     */
493 1
    public function variablize(string $word): string
494
    {
495 1
        $word = $this->camelize($word);
496
497 1
        return mb_strtolower(mb_substr($word, 0, 1)) . mb_substr($word, 1, null);
498
    }
499
500
    /**
501
     * Converts a class name to its table name (pluralized) naming conventions.
502
     *
503
     * For example, converts "Person" to "people".
504
     * @param string $className the class name for getting related table_name
505
     * @return string
506
     */
507 1
    public function tableize(string $className): string
508
    {
509 1
        return $this->pluralize($this->underscore($className));
510
    }
511
512
    /**
513
     * Returns a string with all spaces converted to given replacement,
514
     * non word characters removed and the rest of characters transliterated.
515
     *
516
     * If intl extension isn't available uses fallback that converts latin characters only
517
     * and removes the rest. You may customize characters map via $transliteration property
518
     * of the helper.
519
     *
520
     * @param string $string An arbitrary string to convert
521
     * @param string $replacement The replacement to use for spaces
522
     * @param bool $lowercase whether to return the string in lowercase or not. Defaults to `true`.
523
     * @return string The converted string.
524
     */
525 19
    public function slug(string $string, string $replacement = '-', bool $lowercase = true): string
526
    {
527
        // replace all non words character
528 19
        $string = preg_replace('/[^a-zA-Z0-9]++/u', $replacement, $this->transliterate($string));
529
        // remove first and last replacements
530 19
        $string = preg_replace('/^(?:' . preg_quote($replacement) . ')++|(?:' . preg_quote($replacement) . ')++$/u' . ($lowercase ? 'i' : ''), '', $string);
531
532 19
        return $lowercase ? strtolower($string) : $string;
533
    }
534
535
    /**
536
     * Returns transliterated version of a string.
537
     *
538
     * If intl extension isn't available uses fallback that converts latin characters only
539
     * and removes the rest. You may customize characters map via $transliteration property
540
     * of the helper.
541
     *
542
     * @noinspection PhpComposerExtensionStubsInspection
543
     *
544
     * @param string $string input string
545
     * @param string|\Transliterator $transliterator either a [[\Transliterator]] or a string
546
     * from which a [[\Transliterator]] can be built.
547
     * @return string
548
     */
549 22
    public function transliterate(string $string, $transliterator = null): string
550
    {
551 22
        if ($this->useIntl()) {
552 21
            if ($transliterator === null) {
553 18
                $transliterator = $this->transliterator;
554
            }
555
556
            /* @noinspection PhpComposerExtensionStubsInspection */
557 21
            return transliterator_transliterate($transliterator, $string);
558
        }
559
560 18
        return strtr($string, $this->transliterationMap);
561
    }
562
563
    /**
564
     * @return bool if intl extension should be used
565
     */
566 22
    protected function useIntl(): bool
567
    {
568 22
        return $this->withoutIntl === false && extension_loaded('intl');
569
    }
570
571
    /**
572
     * Converts a table name to its class name.
573
     *
574
     * For example, converts "people" to "Person".
575
     * @param string $tableName
576
     * @return string
577
     */
578 1
    public function classify(string $tableName): string
579
    {
580 1
        return $this->camelize($this->singularize($tableName));
581
    }
582
583
    /**
584
     * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ...
585
     * @param int $number the number to get its ordinal value
586
     * @return string
587
     */
588 1
    public function ordinalize(int $number): ?string
589
    {
590 1
        if (in_array($number % 100, range(11, 13), true)) {
591 1
            return $number . 'th';
592
        }
593 1
        switch ($number % 10) {
594 1
            case 1:
595 1
                return $number . 'st';
596 1
            case 2:
597 1
                return $number . 'nd';
598 1
            case 3:
599 1
                return $number . 'rd';
600
            default:
601 1
                return $number . 'th';
602
        }
603
    }
604
605
    /**
606
     * Converts a list of words into a sentence.
607
     *
608
     * Special treatment is done for the last few words. For example,
609
     *
610
     * ```php
611
     * $words = ['Spain', 'France'];
612
     * echo Inflector::sentence($words);
613
     * // output: Spain and France
614
     *
615
     * $words = ['Spain', 'France', 'Italy'];
616
     * echo Inflector::sentence($words);
617
     * // output: Spain, France and Italy
618
     *
619
     * $words = ['Spain', 'France', 'Italy'];
620
     * echo Inflector::sentence($words, ' & ');
621
     * // output: Spain, France & Italy
622
     * ```
623
     *
624
     * @param array $words the words to be converted into an string
625
     * @param string $twoWordsConnector the string connecting words when there are only two. Default to " and "
626
     * @param string $lastWordConnector the string connecting the last two words. If this is null, it will
627
     * take the value of `$twoWordsConnector`.
628
     * @param string $connector the string connecting words other than those connected by
629
     * $lastWordConnector and $twoWordsConnector
630
     * @return string the generated sentence
631
     */
632 1
    public function sentence(array $words, ?string $twoWordsConnector = ' and ', ?string $lastWordConnector = null, string $connector = ', '): ?string
633
    {
634 1
        if ($lastWordConnector === null) {
635 1
            $lastWordConnector = $twoWordsConnector;
636
        }
637 1
        switch (count($words)) {
638 1
            case 0:
639 1
                return '';
640 1
            case 1:
641 1
                return reset($words);
642 1
            case 2:
643 1
                return implode($twoWordsConnector, $words);
0 ignored issues
show
Bug introduced by
It seems like $twoWordsConnector can also be of type null; however, parameter $glue of implode() 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

643
                return implode(/** @scrutinizer ignore-type */ $twoWordsConnector, $words);
Loading history...
644
            default:
645 1
                return implode($connector, array_slice($words, 0, -1)) . $lastWordConnector . end($words);
646
        }
647
    }
648
}
649