8000 Add Inflector component (from StringUtil of PropertyAccess) · symfony/symfony@8abebf6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8abebf6

Browse files
committed
Add Inflector component (from StringUtil of PropertyAccess)
1 parent 93e09fe commit 8abebf6

File tree

10 files changed

+518
-310
lines changed

10 files changed

+518
-310
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Inflector;
13+
14+
/**
15+
* Converts words between singular and plural forms.
16+
*
17+
* @author Bernhard Schussek <bschussek@gmail.com>
18+
*
19+
* @internal
20+
*/
21+
final class Inflector
22+
{
23+
/**
24+
* Map English plural to singular suffixes.
25+
*
26+
* @var array
27+
*
28+
* @see http://english-zone.com/spelling/plurals.html
29+
* @see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
30+
*/
31+
private static $pluralMap = array(
32+
// First entry: plural suffix, reversed
33+
// Second entry: length of plural suffix
34+
// Third entry: Whether the suffix may succeed a vocal
35+
// Fourth entry: Whether the suffix may succeed a consonant
36+
// Fifth entry: singular suffix, normal
37+
38+
// bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
39+
array('a', 1, true, true, array('on', 'um')),
40+
41+
// nebulae (nebula)
42+
array('ea', 2, true, true, 'a'),
43+
44+
// services (service)
45+
array('secivres', 8, true, true, 'service'),
46+
47+
// mice (mouse), lice (louse)
48+
array('eci', 3, false, true, 'ouse'),
49+
50+
// geese (goose)
51+
array('esee', 4, false, true, 'oose'),
52+
53+
// fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
54+
array('i', 1, true, true, 'us'),
55+
56+
// men (man), women (woman)
57+
array('nem', 3, true, true, 'man'),
58+
59+
// children (child)
60+
array('nerdlihc', 8, true, true, 'child'),
61+
62+
// oxen (ox)
63+
array('nexo', 4, false, false, 'ox'),
64+
65+
// indices (index), appendices (appendix), prices (price)
66+
array('seci', 4, false, true, array('ex', 'ix', 'ice')),
67+
68+
// selfies (selfie)
69+
array('seifles', 7, true, true, 'selfie'),
70+
71+
// movies (movie)
72+
array('seivom', 6, true, true, 'movie'),
73+
74+
// news (news)
75+
array('swen', 4, true, true, 'news'),
76+
77+
// series (series)
78+
array('seires', 6, true, true, 'series'),
79+
80+
// babies (baby)
81+
array('sei', 3, false, true, 'y'),
82+
83+
// accesses (access), addresses (address), kisses (kiss)
84+
array('sess', 4, true, false, 'ss'),
85+
86+
// analyses (analysis), ellipses (ellipsis), funguses (fungus),
87+
// neuroses (neurosis), theses (thesis), emphases (emphasis),
88+
// oases (oasis), crises (crisis), houses (house), bases (base),
89+
// atlases (atlas)
90+
array('ses', 3, true, true, array('s', 'se', 'sis')),
91+
92+
// objectives (objective), alternative (alternatives)
93+
array('sevit', 5, true, true, 'tive'),
94+
95+
// drives (drive)
96+
array('sevird', 6, false, true, 'drive'),
97+
98+
// lives (life), wives (wife)
99+
array('sevi', 4, false, true, 'ife'),
100+
101+
// moves (move)
102+
array('sevom', 5, true, true, 'move'),
103+
104+
// hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
105+
array('sev', 3, true, true, array('f', 've', 'ff')),
106+
107+
// axes (axis), axes (ax), axes (axe)
108+
array('sexa', 4, false, false, array('ax', 'axe', 'axis')),
109+
110+
// indexes (index), matrixes (matrix)
111+
array('sex', 3, true, false, 'x'),
112+
113+
// quizzes (quiz)
114+
array('sezz', 4, true, false, 'z'),
115+
116+
// bureaus (bureau)
117+
array('suae', 4, false, true, 'eau'),
118+
119+
// roses (rose), garages (garage), cassettes (cassette),
120+
// waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
121+
// shoes (shoe)
122+
array('se', 2, true, true, array('', 'e')),
123+
124+
// tags (tag)
125+
array('s', 1, true, true, ''),
126+
127+
// chateaux (chateau)
128+
array('xuae', 4, false, true, 'eau'),
129+
);
130+
131+
/**
132+
* This class should not be instantiated.
133+
*/
134+
private function __construct()
135+
{
136+
}
137+
138+
/**
139+
* Returns the singular form of a word.
140+
*
141+
* If the method can't determine the form with certainty, an array of the
142+
* possible singulars is returned.
143+
*
144+
* @param string $plural A word in plural form
145+
*
146+
* @return string|array The singular form or an array of possible singular
147+
* forms
148+
*
149+
* @internal
150+
*/
151+
public static function singularize($plural)
152+
{
153+
$pluralRev = strrev($plural);
154+
$lowerPluralRev = strtolower($pluralRev);
155+
$pluralLength = strlen($lowerPluralRev);
156+
157+
// The outer loop iterates over the entries of the plural table
158+
// The inner loop $j iterates over the characters of the plural suffix
159+
// in the plural table to compare them with the characters of the actual
160+
// given plural suffix
161+
foreach (self::$pluralMap as $map) {
162+
$suffix = $map[0];
163+
$suffixLength = $map[1];
164+
$j = 0;
165+
166+
// Compare characters in the plural table and of the suffix of the
167+
// given plural one by one
168+
while ($suffix[$j] === $lowerPluralRev[$j]) {
169+
// Let $j point to the next character
170+
++$j;
171+
172+
// Successfully compared the last character
173+
// Add an entry with the singular suffix to the singular array
174+
if ($j === $suffixLength) {
175+
// Is there any character preceding the suffix in the plural string?
176+
if ($j < $pluralLength) {
177+
$nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
178+
179+
if (!$map[2] && $nextIsVocal) {
180+
// suffix may not succeed a vocal but next char is one
181+
break;
182+
}
183+
184+
if (!$map[3] && !$nextIsVocal) {
185+
// suffix may not succeed a consonant but next char is one
186+
break;
187+
}
188+
}
189+
190+
$newBase = substr($plural, 0, $pluralLength - $suffixLength);
191+
$newSuffix = $map[4];
192+
193+
// Check whether the first character in the plural suffix
194+
// is uppercased. If yes, uppercase the first character in
195+
// the singular suffix too
196+
$firstUpper = ctype_upper($pluralRev[$j - 1]);
197+
198+
if (is_array($newSuffix)) {
199+
$singulars = array();
200+
201+
foreach ($newSuffix as $newSuffixEntry) {
202+
$singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
203+
}
204+
205+
return $singulars;
206+
}
207+
208+
return $newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix);
209+
}
210+
211+
// Suffix is longer than word
212+
if ($j === $pluralLength) {
213+
break;
214+
}
215+
}
216+
}
217+
218+
// Convert teeth to tooth, feet to foot
219+
if (false !== ($pos = strpos($plural, 'ee')) && strlen($plural) > 3 && 'feedback' !== $plural) {
220+
return substr_replace($plural, 'oo', $pos, 2);
221+
}
222+
223+
// Assume that plural and singular is identical
224+
return $plural;
225+
}
226+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2012-2016 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Inflector Component
2+
===================
3+
4+
Inflector converts words between their singular and plural forms (English only).
5+
6+
Disclaimer
7+
----------
8+
9+
This component is currently marked as internal. Do not use it in your own code.
10+
Breaking changes may be introduced in the next minor version of Symfony, or the
11+
component itself might even be removed completely.
12+
13+
Resources
14+
---------
15+
16+
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
17+
* [Report issues](https://github.com/symfony/symfony/issues) and
18+
[send Pull Requests](https://github.com/symfony/symfony/pulls)
19+
in the [main Symfony repository](https://github.com/symfony/symfony)

0 commit comments

Comments
 (0)
0