3
* Pluralize and singularize English words.
5
* Used by Cake's naming conventions throughout the framework.
9
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
12
* Licensed under The MIT License
13
* Redistributions of files must retain the above copyright notice.
15
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
16
* @link http://cakephp.org CakePHP(tm) Project
18
* @subpackage cake.cake.libs
19
* @since CakePHP(tm) v 0.2.9
20
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
24
* Pluralize and singularize English words.
26
* Inflector pluralizes and singularizes English nouns.
27
* Used by Cake's naming conventions throughout the framework.
30
* @subpackage cake.cake.libs
31
* @link http://book.cakephp.org/view/1478/Inflector
36
* Plural inflector rules
43
'/(s)tatus$/i' => '\1\2tatuses',
44
'/(quiz)$/i' => '\1zes',
45
'/^(ox)$/i' => '\1\2en',
46
'/([m|l])ouse$/i' => '\1ice',
47
'/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
48
'/(x|ch|ss|sh)$/i' => '\1es',
49
'/([^aeiouy]|qu)y$/i' => '\1ies',
50
'/(hive)$/i' => '\1s',
51
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
53
'/([ti])um$/i' => '\1a',
54
'/(p)erson$/i' => '\1eople',
55
'/(m)an$/i' => '\1en',
56
'/(c)hild$/i' => '\1hildren',
57
'/(buffal|tomat)o$/i' => '\1\2oes',
58
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
60
'/(alias)$/i' => '\1es',
61
'/(ax|cris|test)is$/i' => '\1es',
66
'uninflected' => array(
67
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people'
72
'brother' => 'brothers',
73
'child' => 'children',
74
'corpus' => 'corpuses',
76
'ganglion' => 'ganglions',
79
'graffito' => 'graffiti',
84
'mongoose' => 'mongooses',
89
'occiput' => 'occiputs',
90
'octopus' => 'octopuses',
96
'soliloquy' => 'soliloquies',
98
'trilby' => 'trilbys',
104
* Singular inflector rules
109
var $_singular = array(
111
'/(s)tatuses$/i' => '\1\2tatus',
112
'/^(.*)(menu)s$/i' => '\1\2',
113
'/(quiz)zes$/i' => '\\1',
114
'/(matr)ices$/i' => '\1ix',
115
'/(vert|ind)ices$/i' => '\1ex',
116
'/^(ox)en/i' => '\1',
117
'/(alias)(es)*$/i' => '\1',
118
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
119
'/([ftw]ax)es/i' => '\1',
120
'/(cris|ax|test)es$/i' => '\1is',
121
'/(shoe|slave)s$/i' => '\1',
123
'/ouses$/' => 'ouse',
124
'/([^a])uses$/' => '\1us',
125
'/([m|l])ice$/i' => '\1ouse',
126
'/(x|ch|ss|sh)es$/i' => '\1',
127
'/(m)ovies$/i' => '\1\2ovie',
128
'/(s)eries$/i' => '\1\2eries',
129
'/([^aeiouy]|qu)ies$/i' => '\1y',
130
'/([lr])ves$/i' => '\1f',
131
'/(tive)s$/i' => '\1',
132
'/(hive)s$/i' => '\1',
133
'/(drive)s$/i' => '\1',
134
'/([^fo])ves$/i' => '\1fe',
135
'/(^analy)ses$/i' => '\1sis',
136
'/(analy|ba|diagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
137
'/([ti])a$/i' => '\1um',
138
'/(p)eople$/i' => '\1\2erson',
139
'/(m)en$/i' => '\1an',
140
'/(c)hildren$/i' => '\1\2hild',
141
'/(n)ews$/i' => '\1\2ews',
143
'/^(.*us)$/' => '\\1',
146
'uninflected' => array(
147
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
149
'irregular' => array(
155
* Words that should not be inflected
160
var $_uninflected = array(
161
'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
162
'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
163
'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
164
'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
165
'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
166
'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media',
167
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
168
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
169
'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
170
'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes',
171
'trousers', 'trout','tuna', 'Vermontese', 'Wenchowese', 'whiting', 'wildebeest',
176
* Default map of accented and special characters to ASCII characters
181
var $_transliteration = array(
188
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
189
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
190
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
191
'/ç|ć|ĉ|ċ|č/' => 'c',
194
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
195
'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
200
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
201
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
206
'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
207
'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
209
'/ñ|ń|ņ|ň|ʼn/' => 'n',
210
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
211
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
215
'/ś|ŝ|ş|š|ſ/' => 's',
218
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
219
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
235
* Cached array identity map of pluralized words.
240
var $_pluralized = array();
243
* Cached array identity map of singularized words.
248
var $_singularized = array();
251
* Cached Underscore Inflections
256
var $_underscore = array();
259
* Cached Camelize Inflections
264
var $_camelize = array();
267
* Classify cached inflecctions
272
var $_classify = array();
275
* Tablize cached inflections
280
var $_tableize = array();
283
* Humanize cached inflections
288
var $_humanize = array();
291
* Gets a reference to the Inflector object instance
296
function &getInstance() {
297
static $instance = array();
300
$instance[0] =& new Inflector();
306
* Cache inflected values, and return if already available
308
* @param string $type Inflection type
309
* @param string $key Original value
310
* @param string $value Inflected value
311
* @return string Inflected value, from cache
314
function _cache($type, $key, $value = false) {
317
if ($value !== false) {
318
$this->{$type}[$key] = $value;
322
if (!isset($this->{$type}[$key])) {
325
return $this->{$type}[$key];
329
* Adds custom inflection $rules, of either 'plural', 'singular' or 'transliteration' $type.
334
* Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
335
* Inflector::rules('plural', array(
336
* 'rules' => array('/^(inflect)ors$/i' => '\1ables'),
337
* 'uninflected' => array('dontinflectme'),
338
* 'irregular' => array('red' => 'redlings')
340
* Inflector::rules('transliteration', array('/å/' => 'aa'));
343
* @param string $type The type of inflection, either 'plural', 'singular' or 'transliteration'
344
* @param array $rules Array of rules to be added.
345
* @param boolean $reset If true, will unset default inflections for all
346
* new rules that are being defined in $rules.
351
function rules($type, $rules, $reset = false) {
352
$_this =& Inflector::getInstance();
356
case 'transliteration':
358
$_this->_transliteration = $rules;
360
$_this->_transliteration = $rules + $_this->_transliteration;
365
foreach ($rules as $rule => $pattern) {
366
if (is_array($pattern)) {
368
$_this->{$var}[$rule] = $pattern;
370
$_this->{$var}[$rule] = array_merge($pattern, $_this->{$var}[$rule]);
372
unset($rules[$rule], $_this->{$var}['cache' . ucfirst($rule)]);
373
if (isset($_this->{$var}['merged'][$rule])) {
374
unset($_this->{$var}['merged'][$rule]);
376
if ($type === 'plural') {
377
$_this->_pluralized = $_this->_tableize = array();
378
} elseif ($type === 'singular') {
379
$_this->_singularized = array();
383
$_this->{$var}['rules'] = array_merge($rules, $_this->{$var}['rules']);
389
* Return $word in plural form.
391
* @param string $word Word in singular
392
* @return string Word in plural
395
* @link http://book.cakephp.org/view/1479/Class-methods
397
function pluralize($word) {
398
$_this =& Inflector::getInstance();
400
if (isset($_this->_pluralized[$word])) {
401
return $_this->_pluralized[$word];
404
if (!isset($_this->_plural['merged']['irregular'])) {
405
$_this->_plural['merged']['irregular'] = $_this->_plural['irregular'];
408
if (!isset($_this->plural['merged']['uninflected'])) {
409
$_this->_plural['merged']['uninflected'] = array_merge($_this->_plural['uninflected'], $_this->_uninflected);
412
if (!isset($_this->_plural['cacheUninflected']) || !isset($_this->_plural['cacheIrregular'])) {
413
$_this->_plural['cacheUninflected'] = '(?:' . implode('|', $_this->_plural['merged']['uninflected']) . ')';
414
$_this->_plural['cacheIrregular'] = '(?:' . implode('|', array_keys($_this->_plural['merged']['irregular'])) . ')';
417
if (preg_match('/(.*)\\b(' . $_this->_plural['cacheIrregular'] . ')$/i', $word, $regs)) {
418
$_this->_pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_plural['merged']['irregular'][strtolower($regs[2])], 1);
419
return $_this->_pluralized[$word];
422
if (preg_match('/^(' . $_this->_plural['cacheUninflected'] . ')$/i', $word, $regs)) {
423
$_this->_pluralized[$word] = $word;
427
foreach ($_this->_plural['rules'] as $rule => $replacement) {
428
if (preg_match($rule, $word)) {
429
$_this->_pluralized[$word] = preg_replace($rule, $replacement, $word);
430
return $_this->_pluralized[$word];
436
* Return $word in singular form.
438
* @param string $word Word in plural
439
* @return string Word in singular
442
* @link http://book.cakephp.org/view/1479/Class-methods
444
function singularize($word) {
445
$_this =& Inflector::getInstance();
447
if (isset($_this->_singularized[$word])) {
448
return $_this->_singularized[$word];
451
if (!isset($_this->_singular['merged']['uninflected'])) {
452
$_this->_singular['merged']['uninflected'] = array_merge($_this->_singular['uninflected'], $_this->_uninflected);
455
if (!isset($_this->_singular['merged']['irregular'])) {
456
$_this->_singular['merged']['irregular'] = array_merge($_this->_singular['irregular'], array_flip($_this->_plural['irregular']));
459
if (!isset($_this->_singular['cacheUninflected']) || !isset($_this->_singular['cacheIrregular'])) {
460
$_this->_singular['cacheUninflected'] = '(?:' . join( '|', $_this->_singular['merged']['uninflected']) . ')';
461
$_this->_singular['cacheIrregular'] = '(?:' . join( '|', array_keys($_this->_singular['merged']['irregular'])) . ')';
464
if (preg_match('/(.*)\\b(' . $_this->_singular['cacheIrregular'] . ')$/i', $word, $regs)) {
465
$_this->_singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_singular['merged']['irregular'][strtolower($regs[2])], 1);
466
return $_this->_singularized[$word];
469
if (preg_match('/^(' . $_this->_singular['cacheUninflected'] . ')$/i', $word, $regs)) {
470
$_this->_singularized[$word] = $word;
474
foreach ($_this->_singular['rules'] as $rule => $replacement) {
475
if (preg_match($rule, $word)) {
476
$_this->_singularized[$word] = preg_replace($rule, $replacement, $word);
477
return $_this->_singularized[$word];
480
$_this->_singularized[$word] = $word;
485
* Returns the given lower_case_and_underscored_word as a CamelCased word.
487
* @param string $lower_case_and_underscored_word Word to camelize
488
* @return string Camelized word. LikeThis.
491
* @link http://book.cakephp.org/view/1479/Class-methods
493
function camelize($lowerCaseAndUnderscoredWord) {
494
$_this =& Inflector::getInstance();
495
if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
496
$result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord));
497
$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
503
* Returns the given camelCasedWord as an underscored_word.
505
* @param string $camelCasedWord Camel-cased word to be "underscorized"
506
* @return string Underscore-syntaxed version of the $camelCasedWord
509
* @link http://book.cakephp.org/view/1479/Class-methods
511
function underscore($camelCasedWord) {
512
$_this =& Inflector::getInstance();
513
if (!($result = $_this->_cache(__FUNCTION__, $camelCasedWord))) {
514
$result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
515
$_this->_cache(__FUNCTION__, $camelCasedWord, $result);
521
* Returns the given underscored_word_group as a Human Readable Word Group.
522
* (Underscores are replaced by spaces and capitalized following words.)
524
* @param string $lower_case_and_underscored_word String to be made more readable
525
* @return string Human-readable string
528
* @link http://book.cakephp.org/view/1479/Class-methods
530
function humanize($lowerCaseAndUnderscoredWord) {
531
$_this =& Inflector::getInstance();
532
if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
533
$result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
534
$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
540
* Returns corresponding table name for given model $className. ("people" for the model class "Person").
542
* @param string $className Name of class to get database table name for
543
* @return string Name of the database table for given class
546
* @link http://book.cakephp.org/view/1479/Class-methods
548
function tableize($className) {
549
$_this =& Inflector::getInstance();
550
if (!($result = $_this->_cache(__FUNCTION__, $className))) {
551
$result = Inflector::pluralize(Inflector::underscore($className));
552
$_this->_cache(__FUNCTION__, $className, $result);
558
* Returns Cake model class name ("Person" for the database table "people".) for given database table.
560
* @param string $tableName Name of database table to get class name for
561
* @return string Class name
564
* @link http://book.cakephp.org/view/1479/Class-methods
566
function classify($tableName) {
567
$_this =& Inflector::getInstance();
568
if (!($result = $_this->_cache(__FUNCTION__, $tableName))) {
569
$result = Inflector::camelize(Inflector::singularize($tableName));
570
$_this->_cache(__FUNCTION__, $tableName, $result);
576
* Returns camelBacked version of an underscored string.
578
* @param string $string
579
* @return string in variable form
582
* @link http://book.cakephp.org/view/1479/Class-methods
584
function variable($string) {
585
$_this =& Inflector::getInstance();
586
if (!($result = $_this->_cache(__FUNCTION__, $string))) {
587
$string2 = Inflector::camelize(Inflector::underscore($string));
588
$replace = strtolower(substr($string2, 0, 1));
589
$result = preg_replace('/\\w/', $replace, $string2, 1);
590
$_this->_cache(__FUNCTION__, $string, $result);
596
* Returns a string with all spaces converted to underscores (by default), accented
597
* characters converted to non-accented characters, and non word characters removed.
599
* @param string $string the string you want to slug
600
* @param string $replacement will replace keys in map
601
* @param array $map extra elements to map to the replacement
602
* @deprecated $map param will be removed in future versions. Use Inflector::rules() instead
606
* @link http://book.cakephp.org/view/1479/Class-methods
608
function slug($string, $replacement = '_', $map = array()) {
609
$_this =& Inflector::getInstance();
611
if (is_array($replacement)) {
615
$quotedReplacement = preg_quote($replacement, '/');
618
'/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
619
'/\\s+/' => $replacement,
620
sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
623
$map = $map + $_this->_transliteration + $merge;
624
return preg_replace(array_keys($map), array_values($map), $string);