Completed
Push — master ( 3c3488...5ce4a3 )
by Edson
01:39
created

Inflect::singularize()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 8
nop 1
dl 0
loc 24
ccs 0
cts 17
cp 0
crap 42
rs 8.5125
c 0
b 0
f 0
1
<?php
2
3
namespace Keep;
4
5
// original source: http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/
6
7
/*
8
  The MIT License (MIT)
9
10
  Copyright (c) 2015
11
12
  Permission is hereby granted, free of charge, to any person obtaining a copy
13
  of this software and associated documentation files (the "Software"), to deal
14
  in the Software without restriction, including without limitation the rights
15
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
  copies of the Software, and to permit persons to whom the Software is
17
  furnished to do so, subject to the following conditions:
18
19
  The above copyright notice and this permission notice shall be included in
20
  all copies or substantial portions of the Software.
21
22
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
  THE SOFTWARE.
29
*/
30
31
// ORIGINAL NOTES
32
//
33
// Thanks to http://www.eval.ca/articles/php-pluralize (MIT license)
34
//           http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/inflections.rb (MIT license)
35
//           http://www.fortunecity.com/bally/durrus/153/gramch13.html
36
//           http://www2.gsu.edu/~wwwesl/egw/crump.htm
37
//
38
// Changes (12/17/07)
39
//   Major changes
40
//   --
41
//   Fixed irregular noun algorithm to use regular expressions just like the original Ruby source.
42
//       (this allows for things like fireman -> firemen
43
//   Fixed the order of the singular array, which was backwards.
44
//
45
//   Minor changes
46
//   --
47
//   Removed incorrect pluralization rule for /([^aeiouy]|qu)ies$/ => $1y
48
//   Expanded on the list of exceptions for *o -> *oes, and removed rule for buffalo -> buffaloes
49
//   Removed dangerous singularization rule for /([^f])ves$/ => $1fe
50
//   Added more specific rules for singularizing lives, wives, knives, sheaves, loaves, and leaves and thieves
51
//   Added exception to /(us)es$/ => $1 rule for houses => house and blouses => blouse
52
//   Added excpetions for feet, geese and teeth
53
//   Added rule for deer -> deer
54
55
// Changes:
56
//   Removed rule for virus -> viri
57
//   Added rule for potato -> potatoes
58
//   Added rule for *us -> *uses
59
60
/**
61
 * Class Inflect
62
 * @package Keep
63
 */
64
class Inflect
65
{
66
    /**
67
     * @var array
68
     */
69
    private static $plural = array(
70
        '/(quiz)$/i'               => "$1zes",
71
        '/^(ox)$/i'                => "$1en",
72
        '/([m|l])ouse$/i'          => "$1ice",
73
        '/(matr|vert|ind)ix|ex$/i' => "$1ices",
74
        '/(x|ch|ss|sh)$/i'         => "$1es",
75
        '/([^aeiouy]|qu)y$/i'      => "$1ies",
76
        '/(hive)$/i'               => "$1s",
77
        '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
78
        '/(shea|lea|loa|thie)f$/i' => "$1ves",
79
        '/sis$/i'                  => "ses",
80
        '/([ti])um$/i'             => "$1a",
81
        '/(tomat|potat|ech|her|vet)o$/i'=> "$1oes",
82
        '/(bu)s$/i'                => "$1ses",
83
        '/(alias)$/i'              => "$1es",
84
        '/(octop)us$/i'            => "$1i",
85
        '/(ax|test)is$/i'          => "$1es",
86
        '/(us)$/i'                 => "$1es",
87
        '/s$/i'                    => "s",
88
        '/$/'                      => "s"
89
    );
90
91
    /**
92
     * @var array
93
     */
94
    private static $singular = array(
95
        '/(quiz)zes$/i'             => "$1",
96
        '/(matr)ices$/i'            => "$1ix",
97
        '/(vert|ind)ices$/i'        => "$1ex",
98
        '/^(ox)en$/i'               => "$1",
99
        '/(alias)es$/i'             => "$1",
100
        '/(octop|vir)i$/i'          => "$1us",
101
        '/(cris|ax|test)es$/i'      => "$1is",
102
        '/(shoe)s$/i'               => "$1",
103
        '/(o)es$/i'                 => "$1",
104
        '/(bus)es$/i'               => "$1",
105
        '/([m|l])ice$/i'            => "$1ouse",
106
        '/(x|ch|ss|sh)es$/i'        => "$1",
107
        '/(m)ovies$/i'              => "$1ovie",
108
        '/(s)eries$/i'              => "$1eries",
109
        '/([^aeiouy]|qu)ies$/i'     => "$1y",
110
        '/([lr])ves$/i'             => "$1f",
111
        '/(tive)s$/i'               => "$1",
112
        '/(hive)s$/i'               => "$1",
113
        '/(li|wi|kni)ves$/i'        => "$1fe",
114
        '/(shea|loa|lea|thie)ves$/i'=> "$1f",
115
        '/(^analy)ses$/i'           => "$1sis",
116
        '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i'  => "$1$2sis",
117
        '/([ti])a$/i'               => "$1um",
118
        '/(n)ews$/i'                => "$1ews",
119
        '/(h|bl)ouses$/i'           => "$1ouse",
120
        '/(corpse)s$/i'             => "$1",
121
        '/(us)es$/i'                => "$1",
122
        '/s$/i'                     => ""
123
    );
124
125
    /**
126
     * @var array
127
     */
128
    private static $irregular = array(
129
        'move'   => 'moves',
130
        'foot'   => 'feet',
131
        'goose'  => 'geese',
132
        'sex'    => 'sexes',
133
        'child'  => 'children',
134
        'man'    => 'men',
135
        'tooth'  => 'teeth',
136
        'person' => 'people',
137
        'valve'  => 'valves'
138
    );
139
140
    /**
141
     * @var array
142
     */
143
    private static $uncountable = array(
144
        'sheep',
145
        'fish',
146
        'deer',
147
        'series',
148
        'species',
149
        'money',
150
        'rice',
151
        'information',
152
        'equipment'
153
    );
154
155
    /**
156
     * @param $string
157
     * @return null|string|string[]
158
     */
159
    public static function pluralize($string)
160
    {
161
        // save some time in the case that singular and plural are the same
162
        if (in_array(strtolower($string), self::$uncountable)) {
163
            return $string;
164
        }
165
166
167
        // check for irregular singular forms
168
        foreach (self::$irregular as $pattern => $result) {
169
            $pattern = '/' . $pattern . '$/i';
170
171
            if (preg_match($pattern, $string)) {
172
                return preg_replace($pattern, $result, $string);
173
            }
174
        }
175
176
        // check for matches using regular expressions
177
        foreach (self::$plural as $pattern => $result) {
178
            if (preg_match($pattern, $string)) {
179
                return preg_replace($pattern, $result, $string);
180
            }
181
        }
182
183
        return $string;
184
    }
185
186
    /**
187
     * @param $string
188
     * @return null|string|string[]
189
     */
190
    public static function singularize($string)
191
    {
192
        // save some time in the case that singular and plural are the same
193
        if (in_array(strtolower($string), self::$uncountable)) {
194
            return $string;
195
        }
196
197
        // check for irregular plural forms
198
        foreach (self::$irregular as $result => $pattern) {
199
            $pattern = '/' . $pattern . '$/i';
200
201
            if (preg_match($pattern, $string)) {
202
                return preg_replace($pattern, $result, $string);
203
            }
204
        }
205
206
        // check for matches using regular expressions
207
        foreach (self::$singular as $pattern => $result) {
208
            if (preg_match($pattern, $string)) {
209
                return preg_replace($pattern, $result, $string);
210
            }
211
        }
212
213
        return $string;
214
    }
215
}
216