Completed
Push — master ( 7cb054...584ba8 )
by Tim
44:11 queued 26:38
created

Inflector::singularize()   D

Complexity

Conditions 10
Paths 41

Size

Total Lines 44
Code Lines 26

Duplication

Lines 30
Ratio 68.18 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 10
eloc 26
c 1
b 0
f 1
nc 41
nop 1
dl 30
loc 44
rs 4.8196

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * Copyright (c) Ouzo contributors, http://ouzoframework.org
4
 * This file is made available under the MIT License (view the LICENSE file for more information).
5
 */
6
namespace Ouzo\Utilities;
7
8
/**
9
 * Original Inflector class was taken from Doctrine Inflector and modified later.
10
 * From Doctrine comments: Pluralize & Singularize implementation are borrowed from CakePHP with some modifications.
11
 */
12
class Inflector
13
{
14
    /**
15
     * Plural inflector rules.
16
     *
17
     * @var array
18
     */
19
    private static $plural = array(
20
        'rules' => array(
21
            '/(s)tatus$/i' => '\1\2tatuses',
22
            '/(quiz)$/i' => '\1zes',
23
            '/^(ox)$/i' => '\1\2en',
24
            '/([m|l])ouse$/i' => '\1ice',
25
            '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
26
            '/(x|ch|ss|sh)$/i' => '\1es',
27
            '/([^aeiouy]|qu)y$/i' => '\1ies',
28
            '/(hive)$/i' => '\1s',
29
            '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
30
            '/sis$/i' => 'ses',
31
            '/([ti])um$/i' => '\1a',
32
            '/(p)erson$/i' => '\1eople',
33
            '/(m)an$/i' => '\1en',
34
            '/(c)hild$/i' => '\1hildren',
35
            '/(buffal|tomat)o$/i' => '\1\2oes',
36
            '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
37
            '/us$/i' => 'uses',
38
            '/(alias)$/i' => '\1es',
39
            '/(ax|cris|test)is$/i' => '\1es',
40
            '/s$/' => 's',
41
            '/^$/' => '',
42
            '/$/' => 's',
43
        ),
44
        'uninflected' => array(
45
            '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie'
46
        ),
47
        'irregular' => array(
48
            'atlas' => 'atlases',
49
            'beef' => 'beefs',
50
            'brother' => 'brothers',
51
            'cafe' => 'cafes',
52
            'child' => 'children',
53
            'cookie' => 'cookies',
54
            'corpus' => 'corpuses',
55
            'cow' => 'cows',
56
            'ganglion' => 'ganglions',
57
            'genie' => 'genies',
58
            'genus' => 'genera',
59
            'graffito' => 'graffiti',
60
            'hoof' => 'hoofs',
61
            'loaf' => 'loaves',
62
            'man' => 'men',
63
            'money' => 'monies',
64
            'mongoose' => 'mongooses',
65
            'move' => 'moves',
66
            'mythos' => 'mythoi',
67
            'niche' => 'niches',
68
            'numen' => 'numina',
69
            'occiput' => 'occiputs',
70
            'octopus' => 'octopuses',
71
            'opus' => 'opuses',
72
            'ox' => 'oxen',
73
            'penis' => 'penises',
74
            'person' => 'people',
75
            'sex' => 'sexes',
76
            'soliloquy' => 'soliloquies',
77
            'testis' => 'testes',
78
            'trilby' => 'trilbys',
79
            'turf' => 'turfs',
80
            'foot' => 'feet'
81
        )
82
    );
83
84
    /**
85
     * Singular inflector rules.
86
     *
87
     * @var array
88
     */
89
    private static $singular = array(
90
        'rules' => array(
91
            '/(s)tatuses$/i' => '\1\2tatus',
92
            '/^(.*)(menu)s$/i' => '\1\2',
93
            '/(quiz)zes$/i' => '\\1',
94
            '/(matr)ices$/i' => '\1ix',
95
            '/(vert|ind)ices$/i' => '\1ex',
96
            '/^(ox)en/i' => '\1',
97
            '/(alias)(es)*$/i' => '\1',
98
            '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
99
            '/([ftw]ax)es/i' => '\1',
100
            '/(cris|ax|test)es$/i' => '\1is',
101
            '/(shoe|slave)s$/i' => '\1',
102
            '/(o)es$/i' => '\1',
103
            '/ouses$/' => 'ouse',
104
            '/([^a])uses$/' => '\1us',
105
            '/([m|l])ice$/i' => '\1ouse',
106
            '/(x|ch|ss|sh)es$/i' => '\1',
107
            '/(m)ovies$/i' => '\1\2ovie',
108
            '/(s)eries$/i' => '\1\2eries',
109
            '/([^aeiouy]|qu)ies$/i' => '\1y',
110
            '/([lr])ves$/i' => '\1f',
111
            '/(tive)s$/i' => '\1',
112
            '/(hive)s$/i' => '\1',
113
            '/(drive)s$/i' => '\1',
114
            '/([^fo])ves$/i' => '\1fe',
115
            '/(^analy)ses$/i' => '\1sis',
116
            '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
117
            '/([ti])a$/i' => '\1um',
118
            '/(p)eople$/i' => '\1\2erson',
119
            '/(m)en$/i' => '\1an',
120
            '/(c)hildren$/i' => '\1\2hild',
121
            '/(n)ews$/i' => '\1\2ews',
122
            '/eaus$/' => 'eau',
123
            '/^(.*us)$/' => '\\1',
124
            '/s$/i' => ''
125
        ),
126
        'uninflected' => array(
127
            '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
128
        ),
129
        'irregular' => array(
130
            'foes' => 'foe',
131
            'waves' => 'wave',
132
            'curves' => 'curve'
133
        )
134
    );
135
136
    /**
137
     * Words that should not be inflected.
138
     *
139
     * @var array
140
     */
141
    private static $uninflected = array(
142
        'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
143
        'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
144
        'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
145
        'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
146
        'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
147
        'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media',
148
        'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
149
        'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
150
        'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
151
        'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine',
152
        'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting',
153
        'wildebeest', 'Yengeese'
154
    );
155
156
    /**
157
     * Method cache array.
158
     *
159
     * @var array
160
     */
161
    private static $cache = array();
162
163
    /**
164
     * The initial state of Inflector so reset() works.
165
     *
166
     * @var array
167
     */
168
    private static $initialState = array();
169
170
    /**
171
     * Clears Inflectors inflected value caches, and resets the inflection
172
     * rules to the initial values.
173
     *
174
     * @return void
175
     */
176
    public static function reset()
177
    {
178
        if (empty(self::$initialState)) {
179
            self::$initialState = get_class_vars('Ouzo\Utilities\Inflector');
180
            return;
181
        }
182
        foreach (self::$initialState as $key => $val) {
183
            if ($key != 'initialState') {
184
                self::${$key} = $val;
185
            }
186
        }
187
        self::$cache = array();
188
    }
189
190
    /**
191
     * Adds custom inflection $rules, of either 'plural' or 'singular' $type.
192
     *
193
     * ### Usage:
194
     *
195
     * {{{
196
     * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
197
     * Inflector::rules('plural', array(
198
     *     'rules' => array('/^(inflect)ors$/i' => '\1ables'),
199
     *     'uninflected' => array('dontinflectme'),
200
     *     'irregular' => array('red' => 'redlings')
201
     * ));
202
     * }}}
203
     *
204
     * @param string $type The type of inflection, either 'plural' or 'singular'
205
     * @param array $rules An array of rules to be added.
206
     * @param boolean $reset If true, will unset default inflections for all
207
     *                       new rules that are being defined in $rules.
208
     *
209
     * @return void
210
     */
211
    public static function rules($type, $rules, $reset = false)
212
    {
213
        foreach ($rules as $rule => $pattern) {
214
            if (is_array($pattern)) {
215
                if ($reset) {
216
                    self::${$type}[$rule] = $pattern;
217
                } else {
218
                    if ($rule === 'uninflected') {
219
                        self::${$type}[$rule] = array_merge($pattern, self::${$type}[$rule]);
220
                    } else {
221
                        self::${$type}[$rule] = $pattern + self::${$type}[$rule];
222
                    }
223
                }
224
                unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
225
                if (isset(self::${$type}['merged'][$rule])) {
226
                    unset(self::${$type}['merged'][$rule]);
227
                }
228
                if ($type === 'plural') {
229
                    self::$cache['pluralize'] = self::$cache['tableize'] = array();
230
                } elseif ($type === 'singular') {
231
                    self::$cache['singularize'] = array();
232
                }
233
            }
234
        }
235
        self::${$type}['rules'] = $rules + self::${$type}['rules'];
236
    }
237
238
    /**
239
     * Returns a word in plural form.
240
     *
241
     * @param string $word The word in singular form.
242
     *
243
     * @return string The word in plural form.
244
     */
245
    public static function pluralize($word)
246
    {
247
        if (isset(self::$cache['pluralize'][$word])) {
248
            return self::$cache['pluralize'][$word];
249
        }
250
251
        if (!isset(self::$plural['merged']['irregular'])) {
252
            self::$plural['merged']['irregular'] = self::$plural['irregular'];
253
        }
254
255
        if (!isset(self::$plural['merged']['uninflected'])) {
256
            self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
257
        }
258
259
        if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
260
            self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
261
            self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
262
        }
263
264
        if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
265
            self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
266
            return self::$cache['pluralize'][$word];
267
        }
268
269
        if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
270
            self::$cache['pluralize'][$word] = $word;
271
            return $word;
272
        }
273
274
        foreach (self::$plural['rules'] as $rule => $replacement) {
275
            if (preg_match($rule, $word)) {
276
                self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
277
                return self::$cache['pluralize'][$word];
278
            }
279
        }
280
    }
281
282
    /**
283
     * Returns a word in singular form.
284
     *
285
     * @param string $word The word in plural form.
286
     *
287
     * @return string The word in singular form.
288
     */
289
    public static function singularize($word)
290
    {
291
        if (isset(self::$cache['singularize'][$word])) {
292
            return self::$cache['singularize'][$word];
293
        }
294
295
        if (!isset(self::$singular['merged']['uninflected'])) {
296
            self::$singular['merged']['uninflected'] = array_merge(
297
                self::$singular['uninflected'],
298
                self::$uninflected
299
            );
300
        }
301
302
        if (!isset(self::$singular['merged']['irregular'])) {
303
            self::$singular['merged']['irregular'] = array_merge(
304
                self::$singular['irregular'],
305
                array_flip(self::$plural['irregular'])
306
            );
307
        }
308
309
        if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
310
            self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')';
311
            self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')';
312
        }
313
314
        if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
315
            self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
316
            return self::$cache['singularize'][$word];
317
        }
318
319
        if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
320
            self::$cache['singularize'][$word] = $word;
321
            return $word;
322
        }
323
324
        foreach (self::$singular['rules'] as $rule => $replacement) {
325
            if (preg_match($rule, $word)) {
326
                self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
327
                return self::$cache['singularize'][$word];
328
            }
329
        }
330
        self::$cache['singularize'][$word] = $word;
331
        return $word;
332
    }
333
}
334