Skip to content

Commit bd56ef7

Browse files
committed
Add Inflector component (from StringUtil of PropertyAccess)
1 parent cecaaad commit bd56ef7

File tree

4 files changed

+17
-310
lines changed

4 files changed

+17
-310
lines changed

PropertyAccessor.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\PropertyAccess;
1313

14+
use Symfony\Component\Inflector\Inflector;
1415
use Symfony\Component\PropertyAccess\Exception\AccessException;
1516
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
1617
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
@@ -680,7 +681,7 @@ private function getWriteAccessInfo($class, $property, $value)
680681
$reflClass = new \ReflectionClass($class);
681682
$access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
682683
$camelized = $this->camelize($property);
683-
$singulars = (array) StringUtil::singularify($camelized);
684+
$singulars = (array) Inflector::singularize($camelized);
684685

685686
if (is_array($value) || $value instanceof \Traversable) {
686687
$methods = $this->findAdderAndRemover($reflClass, $singulars);

StringUtil.php

Lines changed: 8 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -11,121 +11,17 @@
1111

1212
namespace Symfony\Component\PropertyAccess;
1313

14+
use Symfony\Component\Inflector\Inflector;
15+
1416
/**
1517
* Creates singulars from plurals.
1618
*
1719
* @author Bernhard Schussek <[email protected]>
20+
*
21+
* @deprecated Deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector} instead.
1822
*/
1923
class StringUtil
2024
{
21-
/**
22-
* Map english plural to singular suffixes.
23-
*
24-
* @var array
25-
*
26-
* @see http://english-zone.com/spelling/plurals.html
27-
* @see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
28-
*/
29-
private static $pluralMap = array(
30-
// First entry: plural suffix, reversed
31-
// Second entry: length of plural suffix
32-
// Third entry: Whether the suffix may succeed a vocal
33-
// Fourth entry: Whether the suffix may succeed a consonant
34-
// Fifth entry: singular suffix, normal
35-
36-
// bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
37-
array('a', 1, true, true, array('on', 'um')),
38-
39-
// nebulae (nebula)
40-
array('ea', 2, true, true, 'a'),
41-
42-
// services (service)
43-
array('secivres', 8, true, true, 'service'),
44-
45-
// mice (mouse), lice (louse)
46-
array('eci', 3, false, true, 'ouse'),
47-
48-
// geese (goose)
49-
array('esee', 4, false, true, 'oose'),
50-
51-
// fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
52-
array('i', 1, true, true, 'us'),
53-
54-
// men (man), women (woman)
55-
array('nem', 3, true, true, 'man'),
56-
57-
// children (child)
58-
array('nerdlihc', 8, true, true, 'child'),
59-
60-
// oxen (ox)
61-
array('nexo', 4, false, false, 'ox'),
62-
63-
// indices (index), appendices (appendix), prices (price)
64-
array('seci', 4, false, true, array('ex', 'ix', 'ice')),
65-
66-
// selfies (selfie)
67-
array('seifles', 7, true, true, 'selfie'),
68-
69-
// movies (movie)
70-
array('seivom', 6, true, true, 'movie'),
71-
72-
// news (news)
73-
array('swen', 4, true, true, 'news'),
74-
75-
// series (series)
76-
array('seires', 6, true, true, 'series'),
77-
78-
// babies (baby)
79-
array('sei', 3, false, true, 'y'),
80-
81-
// accesses (access), addresses (address), kisses (kiss)
82-
array('sess', 4, true, false, 'ss'),
83-
84-
// analyses (analysis), ellipses (ellipsis), funguses (fungus),
85-
// neuroses (neurosis), theses (thesis), emphases (emphasis),
86-
// oases (oasis), crises (crisis), houses (house), bases (base),
87-
// atlases (atlas)
88-
array('ses', 3, true, true, array('s', 'se', 'sis')),
89-
90-
// objectives (objective), alternative (alternatives)
91-
array('sevit', 5, true, true, 'tive'),
92-
93-
// drives (drive)
94-
array('sevird', 6, false, true, 'drive'),
95-
96-
// lives (life), wives (wife)
97-
array('sevi', 4, false, true, 'ife'),
98-
99-
// moves (move)
100-
array('sevom', 5, true, true, 'move'),
101-
102-
// hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
103-
array('sev', 3, true, true, array('f', 've', 'ff')),
104-
105-
// axes (axis), axes (ax), axes (axe)
106-
array('sexa', 4, false, false, array('ax', 'axe', 'axis')),
107-
108-
// indexes (index), matrixes (matrix)
109-
array('sex', 3, true, false, 'x'),
110-
111-
// quizzes (quiz)
112-
array('sezz', 4, true, false, 'z'),
113-
114-
// bureaus (bureau)
115-
array('suae', 4, false, true, 'eau'),
116-
117-
// roses (rose), garages (garage), cassettes (cassette),
118-
// waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
119-
// shoes (shoe)
120-
array('se', 2, true, true, array('', 'e')),
121-
122-
// tags (tag)
123-
array('s', 1, true, true, ''),
124-
125-
// chateaux (chateau)
126-
array('xuae', 4, false, true, 'eau'),
127-
);
128-
12925
/**
13026
* This class should not be instantiated.
13127
*/
@@ -143,80 +39,13 @@ private function __construct()
14339
*
14440
* @return string|array The singular form or an array of possible singular
14541
* forms
42+
*
43+
* @deprecated Deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector::singularize} instead.
14644
*/
14745
public static function singularify($plural)
14846
{
149-
$pluralRev = strrev($plural);
150-
$lowerPluralRev = strtolower($pluralRev);
151-
$pluralLength = strlen($lowerPluralRev);
152-
153-
// The outer loop iterates over the entries of the plural table
154-
// The inner loop $j iterates over the characters of the plural suffix
155-
// in the plural table to compare them with the characters of the actual
156-
// given plural suffix
157-
foreach (self::$pluralMap as $map) {
158-
$suffix = $map[0];
159-
$suffixLength = $map[1];
160-
$j = 0;
161-
162-
// Compare characters in the plural table and of the suffix of the
163-
// given plural one by one
164-
while ($suffix[$j] === $lowerPluralRev[$j]) {
165-
// Let $j point to the next character
166-
++$j;
167-
168-
// Successfully compared the last character
169-
// Add an entry with the singular suffix to the singular array
170-
if ($j === $suffixLength) {
171-
// Is there any character preceding the suffix in the plural string?
172-
if ($j < $pluralLength) {
173-
$nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
174-
175-
if (!$map[2] && $nextIsVocal) {
176-
// suffix may not succeed a vocal but next char is one
177-
break;
178-
}
179-
180-
if (!$map[3] && !$nextIsVocal) {
181-
// suffix may not succeed a consonant but next char is one
182-
break;
183-
}
184-
}
185-
186-
$newBase = substr($plural, 0, $pluralLength - $suffixLength);
187-
$newSuffix = $map[4];
188-
189-
// Check whether the first character in the plural suffix
190-
// is uppercased. If yes, uppercase the first character in
191-
// the singular suffix too
192-
$firstUpper = ctype_upper($pluralRev[$j - 1]);
193-
194-
if (is_array($newSuffix)) {
195-
$singulars = array();
196-
197-
foreach ($newSuffix as $newSuffixEntry) {
198-
$singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
199-
}
200-
201-
return $singulars;
202-
}
203-
204-
return $newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix);
205-
}
206-
207-
// Suffix is longer than word
208-
if ($j === $pluralLength) {
209-
break;
210-
}
211-
}
212-
}
213-
214-
// Convert teeth to tooth, feet to foot
215-
if (false !== ($pos = strpos($plural, 'ee')) && strlen($plural) > 3 && 'feedback' !== $plural) {
216-
return substr_replace($plural, 'oo', $pos, 2);
217-
}
47+
@trigger_error('StringUtil::singularify() is deprecated since version 3.1 and will be removed in 4.0. Use Symfony\Component\Inflector\Inflector::singularize instead.', E_USER_DEPRECATED);
21848

219-
// Assume that plural and singular is identical
220-
return $plural;
49+
return Inflector::singularize($plural);
22150
}
22251
}

Tests/StringUtilTest.php

Lines changed: 5 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -13,141 +13,17 @@
1313

1414
use Symfony\Component\PropertyAccess\StringUtil;
1515

16+
/**
17+
* @group legacy
18+
*/
1619
class StringUtilTest extends \PHPUnit_Framework_TestCase
1720
{
1821
public function singularifyProvider()
1922
{
20-
// see http://english-zone.com/spelling/plurals.html
21-
// see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
23+
// This is only a stub to make sure the BC layer works
24+
// Actual tests are in the Symfony Inflector component
2225
return array(
23-
array('accesses', 'access'),
24-
array('addresses', 'address'),
25-
array('agendas', 'agenda'),
26-
array('alumnae', 'alumna'),
27-
array('alumni', 'alumnus'),
28-
array('analyses', array('analys', 'analyse', 'analysis')),
29-
array('antennae', 'antenna'),
30-
array('antennas', 'antenna'),
31-
array('appendices', array('appendex', 'appendix', 'appendice')),
32-
array('arches', array('arch', 'arche')),
33-
array('atlases', array('atlas', 'atlase', 'atlasis')),
3426
array('axes', array('ax', 'axe', 'axis')),
35-
array('babies', 'baby'),
36-
array('bacteria', array('bacterion', 'bacterium')),
37-
array('bases', array('bas', 'base', 'basis')),
38-
array('batches', array('batch', 'batche')),
39-
array('beaux', 'beau'),
40-
array('bees', array('be', 'bee')),
41-
array('boxes', 'box'),
42-
array('boys', 'boy'),
43-
array('bureaus', 'bureau'),
44-
array('bureaux', 'bureau'),
45-
array('buses', array('bus', 'buse', 'busis')),
46-
array('bushes', array('bush', 'bushe')),
47-
array('calves', array('calf', 'calve', 'calff')),
48-
array('cars', 'car'),
49-
array('cassettes', array('cassett', 'cassette')),
50-
array('caves', array('caf', 'cave', 'caff')),
51-
array('chateaux', 'chateau'),
52-
array('cheeses', array('chees', 'cheese', 'cheesis')),
53-
array('children', 'child'),
54-
array('circuses', array('circus', 'circuse', 'circusis')),
55-
array('cliffs', 'cliff'),
56-
array('crises', array('cris', 'crise', 'crisis')),
57-
array('criteria', array('criterion', 'criterium')),
58-
array('cups', 'cup'),
59-
array('data', array('daton', 'datum')),
60-
array('days', 'day'),
61-
array('discos', 'disco'),
62-
array('devices', array('devex', 'devix', 'device')),
63-
array('drives', 'drive'),
64-
array('drivers', 'driver'),
65-
array('dwarves', array('dwarf', 'dwarve', 'dwarff')),
66-
array('echoes', array('echo', 'echoe')),
67-
array('elves', array('elf', 'elve', 'elff')),
68-
array('emphases', array('emphas', 'emphase', 'emphasis')),
69-
array('faxes', 'fax'),
70-
array('feet', 'foot'),
71-
array('feedback', 'feedback'),
72-
array('foci', 'focus'),
73-
array('focuses', array('focus', 'focuse', 'focusis')),
74-
array('formulae', 'formula'),
75-
array('formulas', 'formula'),
76-
array('fungi', 'fungus'),
77-
array('funguses', array('fungus', 'funguse', 'fungusis')),
78-
array('garages', array('garag', 'garage')),
79-
array('geese', 'goose'),
80-
array('halves', array('half', 'halve', 'halff')),
81-
array('hats', 'hat'),
82-
array('heroes', array('hero', 'heroe')),
83-
array('hippopotamuses', array('hippopotamus', 'hippopotamuse', 'hippopotamusis')), //hippopotami
84-
array('hoaxes', 'hoax'),
85-
array('hooves', array('hoof', 'hoove', 'hooff')),
86-
array('houses', array('hous', 'house', 'housis')),
87-
array('indexes', 'index'),
88-
array('indices', array('index', 'indix', 'indice')),
89-
array('ions', 'ion'),
90-
array('irises', array('iris', 'irise', 'irisis')),
91-
array('kisses', 'kiss'),
92-
array('knives', 'knife'),
93-
array('lamps', 'lamp'),
94-
array('leaves', array('leaf', 'leave', 'leaff')),
95-
array('lice', 'louse'),
96-
array('lives', 'life'),
97-
array('matrices', array('matrex', 'matrix', 'matrice')),
98-
array('matrixes', 'matrix'),
99-
array('men', 'man'),
100-
array('mice', 'mouse'),
101-
array('moves', 'move'),
102-
array('movies', 'movie'),
103-
array('nebulae', 'nebula'),
104-
array('neuroses', array('neuros', 'neurose', 'neurosis')),
105-
array('news', 'news'),
106-
array('oases', array('oas', 'oase', 'oasis')),
107-
array('objectives', 'objective'),
108-
array('oxen', 'ox'),
109-
array('parties', 'party'),
110-
array('phenomena', array('phenomenon', 'phenomenum')),
111-
array('photos', 'photo'),
112-
array('pianos', 'piano'),
113-
array('plateaux', 'plateau'),
114-
array('poppies', 'poppy'),
115-
array('prices', array('prex', 'prix', 'price')),
116-
array('quizzes', 'quiz'),
117-
array('radii', 'radius'),
118-
array('roofs', 'roof'),
119-
array('roses', array('ros', 'rose', 'rosis')),
120-
array('sandwiches', array('sandwich', 'sandwiche')),
121-
array('scarves', array('scarf', 'scarve', 'scarff')),
122-
array('schemas', 'schema'), //schemata
123-
array('selfies', 'selfie'),
124-
array('series', 'series'),
125-
array('services', 'service'),
126-
array('sheriffs', 'sheriff'),
127-
array('shoes', array('sho', 'shoe')),
128-
array('spies', 'spy'),
129-
array('staves', array('staf', 'stave', 'staff')),
130-
array('stories', 'story'),
131-
array('strata', array('straton', 'stratum')),
132-
array('suitcases', array('suitcas', 'suitcase', 'suitcasis')),
133-
array('syllabi', 'syllabus'),
134-
array('tags', 'tag'),
135-
array('teeth', 'tooth'),
136-
array('theses', array('thes', 'these', 'thesis')),
137-
array('thieves', array('thief', 'thieve', 'thieff')),
138-
array('trees', array('tre', 'tree')),
139-
array('waltzes', array('waltz', 'waltze')),
140-
array('wives', 'wife'),
141-
142-
// test casing: if the first letter was uppercase, it should remain so
143-
array('Men', 'Man'),
144-
array('GrandChildren', 'GrandChild'),
145-
array('SubTrees', array('SubTre', 'SubTree')),
146-
147-
// Known issues
148-
//array('insignia', 'insigne'),
149-
//array('insignias', 'insigne'),
150-
//array('rattles', 'rattle'),
15127
);
15228
}
15329

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/polyfill-php70": "~1.0"
20+
"symfony/polyfill-php70": "~1.0",
21+
"symfony/inflector": "~3.1"
2122
},
2223
"autoload": {
2324
"psr-4": { "Symfony\\Component\\PropertyAccess\\": "" },

0 commit comments

Comments
 (0)