1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15: namespace Cake\Utility;
16:
17: 18: 19: 20: 21: 22: 23: 24:
25: class Inflector
26: {
27:
28: 29: 30: 31: 32:
33: protected static $_plural = [
34: '/(s)tatus$/i' => '\1tatuses',
35: '/(quiz)$/i' => '\1zes',
36: '/^(ox)$/i' => '\1\2en',
37: '/([m|l])ouse$/i' => '\1ice',
38: '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
39: '/(x|ch|ss|sh)$/i' => '\1es',
40: '/([^aeiouy]|qu)y$/i' => '\1ies',
41: '/(hive)$/i' => '\1s',
42: '/(chef)$/i' => '\1s',
43: '/(?:([^f])fe|([lre])f)$/i' => '\1\2ves',
44: '/sis$/i' => 'ses',
45: '/([ti])um$/i' => '\1a',
46: '/(p)erson$/i' => '\1eople',
47: '/(?<!u)(m)an$/i' => '\1en',
48: '/(c)hild$/i' => '\1hildren',
49: '/(buffal|tomat)o$/i' => '\1\2oes',
50: '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin)us$/i' => '\1i',
51: '/us$/i' => 'uses',
52: '/(alias)$/i' => '\1es',
53: '/(ax|cris|test)is$/i' => '\1es',
54: '/s$/' => 's',
55: '/^$/' => '',
56: '/$/' => 's',
57: ];
58:
59: 60: 61: 62: 63:
64: protected static $_singular = [
65: '/(s)tatuses$/i' => '\1\2tatus',
66: '/^(.*)(menu)s$/i' => '\1\2',
67: '/(quiz)zes$/i' => '\\1',
68: '/(matr)ices$/i' => '\1ix',
69: '/(vert|ind)ices$/i' => '\1ex',
70: '/^(ox)en/i' => '\1',
71: '/(alias)(es)*$/i' => '\1',
72: '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
73: '/([ftw]ax)es/i' => '\1',
74: '/(cris|ax|test)es$/i' => '\1is',
75: '/(shoe)s$/i' => '\1',
76: '/(o)es$/i' => '\1',
77: '/ouses$/' => 'ouse',
78: '/([^a])uses$/' => '\1us',
79: '/([m|l])ice$/i' => '\1ouse',
80: '/(x|ch|ss|sh)es$/i' => '\1',
81: '/(m)ovies$/i' => '\1\2ovie',
82: '/(s)eries$/i' => '\1\2eries',
83: '/([^aeiouy]|qu)ies$/i' => '\1y',
84: '/(tive)s$/i' => '\1',
85: '/(hive)s$/i' => '\1',
86: '/(drive)s$/i' => '\1',
87: '/([le])ves$/i' => '\1f',
88: '/([^rfoa])ves$/i' => '\1fe',
89: '/(^analy)ses$/i' => '\1sis',
90: '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
91: '/([ti])a$/i' => '\1um',
92: '/(p)eople$/i' => '\1\2erson',
93: '/(m)en$/i' => '\1an',
94: '/(c)hildren$/i' => '\1\2hild',
95: '/(n)ews$/i' => '\1\2ews',
96: '/eaus$/' => 'eau',
97: '/^(.*us)$/' => '\\1',
98: '/s$/i' => ''
99: ];
100:
101: 102: 103: 104: 105:
106: protected static $_irregular = [
107: 'atlas' => 'atlases',
108: 'beef' => 'beefs',
109: 'brief' => 'briefs',
110: 'brother' => 'brothers',
111: 'cafe' => 'cafes',
112: 'child' => 'children',
113: 'cookie' => 'cookies',
114: 'corpus' => 'corpuses',
115: 'cow' => 'cows',
116: 'criterion' => 'criteria',
117: 'ganglion' => 'ganglions',
118: 'genie' => 'genies',
119: 'genus' => 'genera',
120: 'graffito' => 'graffiti',
121: 'hoof' => 'hoofs',
122: 'loaf' => 'loaves',
123: 'man' => 'men',
124: 'money' => 'monies',
125: 'mongoose' => 'mongooses',
126: 'move' => 'moves',
127: 'mythos' => 'mythoi',
128: 'niche' => 'niches',
129: 'numen' => 'numina',
130: 'occiput' => 'occiputs',
131: 'octopus' => 'octopuses',
132: 'opus' => 'opuses',
133: 'ox' => 'oxen',
134: 'penis' => 'penises',
135: 'person' => 'people',
136: 'sex' => 'sexes',
137: 'soliloquy' => 'soliloquies',
138: 'testis' => 'testes',
139: 'trilby' => 'trilbys',
140: 'turf' => 'turfs',
141: 'potato' => 'potatoes',
142: 'hero' => 'heroes',
143: 'tooth' => 'teeth',
144: 'goose' => 'geese',
145: 'foot' => 'feet',
146: 'foe' => 'foes',
147: 'sieve' => 'sieves',
148: 'cache' => 'caches',
149: ];
150:
151: 152: 153: 154: 155:
156: protected static $_uninflected = [
157: '.*[nrlm]ese', '.*data', '.*deer', '.*fish', '.*measles', '.*ois',
158: '.*pox', '.*sheep', 'people', 'feedback', 'stadia', '.*?media',
159: 'chassis', 'clippers', 'debris', 'diabetes', 'equipment', 'gallows',
160: 'graffiti', 'headquarters', 'information', 'innings', 'news', 'nexus',
161: 'pokemon', 'proceedings', 'research', 'sea[- ]bass', 'series', 'species', 'weather'
162: ];
163:
164: 165: 166: 167: 168:
169: protected static $_transliteration = [
170: 'ä' => 'ae',
171: 'æ' => 'ae',
172: 'ǽ' => 'ae',
173: 'ö' => 'oe',
174: 'œ' => 'oe',
175: 'ü' => 'ue',
176: 'Ä' => 'Ae',
177: 'Ü' => 'Ue',
178: 'Ö' => 'Oe',
179: 'À' => 'A',
180: 'Á' => 'A',
181: 'Â' => 'A',
182: 'Ã' => 'A',
183: 'Å' => 'A',
184: 'Ǻ' => 'A',
185: 'Ā' => 'A',
186: 'Ă' => 'A',
187: 'Ą' => 'A',
188: 'Ǎ' => 'A',
189: 'à' => 'a',
190: 'á' => 'a',
191: 'â' => 'a',
192: 'ã' => 'a',
193: 'å' => 'a',
194: 'ǻ' => 'a',
195: 'ā' => 'a',
196: 'ă' => 'a',
197: 'ą' => 'a',
198: 'ǎ' => 'a',
199: 'ª' => 'a',
200: 'Ç' => 'C',
201: 'Ć' => 'C',
202: 'Ĉ' => 'C',
203: 'Ċ' => 'C',
204: 'Č' => 'C',
205: 'ç' => 'c',
206: 'ć' => 'c',
207: 'ĉ' => 'c',
208: 'ċ' => 'c',
209: 'č' => 'c',
210: 'Ð' => 'D',
211: 'Ď' => 'D',
212: 'Đ' => 'D',
213: 'ð' => 'd',
214: 'ď' => 'd',
215: 'đ' => 'd',
216: 'È' => 'E',
217: 'É' => 'E',
218: 'Ê' => 'E',
219: 'Ë' => 'E',
220: 'Ē' => 'E',
221: 'Ĕ' => 'E',
222: 'Ė' => 'E',
223: 'Ę' => 'E',
224: 'Ě' => 'E',
225: 'è' => 'e',
226: 'é' => 'e',
227: 'ê' => 'e',
228: 'ë' => 'e',
229: 'ē' => 'e',
230: 'ĕ' => 'e',
231: 'ė' => 'e',
232: 'ę' => 'e',
233: 'ě' => 'e',
234: 'Ĝ' => 'G',
235: 'Ğ' => 'G',
236: 'Ġ' => 'G',
237: 'Ģ' => 'G',
238: 'Ґ' => 'G',
239: 'ĝ' => 'g',
240: 'ğ' => 'g',
241: 'ġ' => 'g',
242: 'ģ' => 'g',
243: 'ґ' => 'g',
244: 'Ĥ' => 'H',
245: 'Ħ' => 'H',
246: 'ĥ' => 'h',
247: 'ħ' => 'h',
248: 'І' => 'I',
249: 'Ì' => 'I',
250: 'Í' => 'I',
251: 'Î' => 'I',
252: 'Ї' => 'Yi',
253: 'Ï' => 'I',
254: 'Ĩ' => 'I',
255: 'Ī' => 'I',
256: 'Ĭ' => 'I',
257: 'Ǐ' => 'I',
258: 'Į' => 'I',
259: 'İ' => 'I',
260: 'і' => 'i',
261: 'ì' => 'i',
262: 'í' => 'i',
263: 'î' => 'i',
264: 'ï' => 'i',
265: 'ї' => 'yi',
266: 'ĩ' => 'i',
267: 'ī' => 'i',
268: 'ĭ' => 'i',
269: 'ǐ' => 'i',
270: 'į' => 'i',
271: 'ı' => 'i',
272: 'Ĵ' => 'J',
273: 'ĵ' => 'j',
274: 'Ķ' => 'K',
275: 'ķ' => 'k',
276: 'Ĺ' => 'L',
277: 'Ļ' => 'L',
278: 'Ľ' => 'L',
279: 'Ŀ' => 'L',
280: 'Ł' => 'L',
281: 'ĺ' => 'l',
282: 'ļ' => 'l',
283: 'ľ' => 'l',
284: 'ŀ' => 'l',
285: 'ł' => 'l',
286: 'Ñ' => 'N',
287: 'Ń' => 'N',
288: 'Ņ' => 'N',
289: 'Ň' => 'N',
290: 'ñ' => 'n',
291: 'ń' => 'n',
292: 'ņ' => 'n',
293: 'ň' => 'n',
294: 'ʼn' => 'n',
295: 'Ò' => 'O',
296: 'Ó' => 'O',
297: 'Ô' => 'O',
298: 'Õ' => 'O',
299: 'Ō' => 'O',
300: 'Ŏ' => 'O',
301: 'Ǒ' => 'O',
302: 'Ő' => 'O',
303: 'Ơ' => 'O',
304: 'Ø' => 'O',
305: 'Ǿ' => 'O',
306: 'ò' => 'o',
307: 'ó' => 'o',
308: 'ô' => 'o',
309: 'õ' => 'o',
310: 'ō' => 'o',
311: 'ŏ' => 'o',
312: 'ǒ' => 'o',
313: 'ő' => 'o',
314: 'ơ' => 'o',
315: 'ø' => 'o',
316: 'ǿ' => 'o',
317: 'º' => 'o',
318: 'Ŕ' => 'R',
319: 'Ŗ' => 'R',
320: 'Ř' => 'R',
321: 'ŕ' => 'r',
322: 'ŗ' => 'r',
323: 'ř' => 'r',
324: 'Ś' => 'S',
325: 'Ŝ' => 'S',
326: 'Ş' => 'S',
327: 'Ș' => 'S',
328: 'Š' => 'S',
329: 'ẞ' => 'SS',
330: 'ś' => 's',
331: 'ŝ' => 's',
332: 'ş' => 's',
333: 'ș' => 's',
334: 'š' => 's',
335: 'ſ' => 's',
336: 'Ţ' => 'T',
337: 'Ț' => 'T',
338: 'Ť' => 'T',
339: 'Ŧ' => 'T',
340: 'ţ' => 't',
341: 'ț' => 't',
342: 'ť' => 't',
343: 'ŧ' => 't',
344: 'Ù' => 'U',
345: 'Ú' => 'U',
346: 'Û' => 'U',
347: 'Ũ' => 'U',
348: 'Ū' => 'U',
349: 'Ŭ' => 'U',
350: 'Ů' => 'U',
351: 'Ű' => 'U',
352: 'Ų' => 'U',
353: 'Ư' => 'U',
354: 'Ǔ' => 'U',
355: 'Ǖ' => 'U',
356: 'Ǘ' => 'U',
357: 'Ǚ' => 'U',
358: 'Ǜ' => 'U',
359: 'ù' => 'u',
360: 'ú' => 'u',
361: 'û' => 'u',
362: 'ũ' => 'u',
363: 'ū' => 'u',
364: 'ŭ' => 'u',
365: 'ů' => 'u',
366: 'ű' => 'u',
367: 'ų' => 'u',
368: 'ư' => 'u',
369: 'ǔ' => 'u',
370: 'ǖ' => 'u',
371: 'ǘ' => 'u',
372: 'ǚ' => 'u',
373: 'ǜ' => 'u',
374: 'Ý' => 'Y',
375: 'Ÿ' => 'Y',
376: 'Ŷ' => 'Y',
377: 'ý' => 'y',
378: 'ÿ' => 'y',
379: 'ŷ' => 'y',
380: 'Ŵ' => 'W',
381: 'ŵ' => 'w',
382: 'Ź' => 'Z',
383: 'Ż' => 'Z',
384: 'Ž' => 'Z',
385: 'ź' => 'z',
386: 'ż' => 'z',
387: 'ž' => 'z',
388: 'Æ' => 'AE',
389: 'Ǽ' => 'AE',
390: 'ß' => 'ss',
391: 'IJ' => 'IJ',
392: 'ij' => 'ij',
393: 'Œ' => 'OE',
394: 'ƒ' => 'f',
395: 'Þ' => 'TH',
396: 'þ' => 'th',
397: 'Є' => 'Ye',
398: 'є' => 'ye',
399: ];
400:
401: 402: 403: 404: 405:
406: protected static $_cache = [];
407:
408: 409: 410: 411: 412:
413: protected static $_initialState = [];
414:
415: 416: 417: 418: 419: 420: 421: 422:
423: protected static function _cache($type, $key, $value = false)
424: {
425: $key = '_' . $key;
426: $type = '_' . $type;
427: if ($value !== false) {
428: static::$_cache[$type][$key] = $value;
429:
430: return $value;
431: }
432: if (!isset(static::$_cache[$type][$key])) {
433: return false;
434: }
435:
436: return static::$_cache[$type][$key];
437: }
438:
439: 440: 441: 442: 443: 444:
445: public static function reset()
446: {
447: if (empty(static::$_initialState)) {
448: static::$_initialState = get_class_vars(__CLASS__);
449:
450: return;
451: }
452: foreach (static::$_initialState as $key => $val) {
453: if ($key !== '_initialState') {
454: static::${$key} = $val;
455: }
456: }
457: }
458:
459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478:
479: public static function rules($type, $rules, $reset = false)
480: {
481: $var = '_' . $type;
482:
483: if ($reset) {
484: static::${$var} = $rules;
485: } elseif ($type === 'uninflected') {
486: static::$_uninflected = array_merge(
487: $rules,
488: static::$_uninflected
489: );
490: } else {
491: static::${$var} = $rules + static::${$var};
492: }
493:
494: static::$_cache = [];
495: }
496:
497: 498: 499: 500: 501: 502: 503:
504: public static function pluralize($word)
505: {
506: if (isset(static::$_cache['pluralize'][$word])) {
507: return static::$_cache['pluralize'][$word];
508: }
509:
510: if (!isset(static::$_cache['irregular']['pluralize'])) {
511: static::$_cache['irregular']['pluralize'] = '(?:' . implode('|', array_keys(static::$_irregular)) . ')';
512: }
513:
514: if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['pluralize'] . ')$/i', $word, $regs)) {
515: static::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
516: substr(static::$_irregular[strtolower($regs[2])], 1);
517:
518: return static::$_cache['pluralize'][$word];
519: }
520:
521: if (!isset(static::$_cache['uninflected'])) {
522: static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
523: }
524:
525: if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
526: static::$_cache['pluralize'][$word] = $word;
527:
528: return $word;
529: }
530:
531: foreach (static::$_plural as $rule => $replacement) {
532: if (preg_match($rule, $word)) {
533: static::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
534:
535: return static::$_cache['pluralize'][$word];
536: }
537: }
538: }
539:
540: 541: 542: 543: 544: 545: 546:
547: public static function singularize($word)
548: {
549: if (isset(static::$_cache['singularize'][$word])) {
550: return static::$_cache['singularize'][$word];
551: }
552:
553: if (!isset(static::$_cache['irregular']['singular'])) {
554: static::$_cache['irregular']['singular'] = '(?:' . implode('|', static::$_irregular) . ')';
555: }
556:
557: if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['singular'] . ')$/i', $word, $regs)) {
558: static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
559: substr(array_search(strtolower($regs[2]), static::$_irregular), 1);
560:
561: return static::$_cache['singularize'][$word];
562: }
563:
564: if (!isset(static::$_cache['uninflected'])) {
565: static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
566: }
567:
568: if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
569: static::$_cache['pluralize'][$word] = $word;
570:
571: return $word;
572: }
573:
574: foreach (static::$_singular as $rule => $replacement) {
575: if (preg_match($rule, $word)) {
576: static::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
577:
578: return static::$_cache['singularize'][$word];
579: }
580: }
581: static::$_cache['singularize'][$word] = $word;
582:
583: return $word;
584: }
585:
586: 587: 588: 589: 590: 591: 592: 593:
594: public static function camelize($string, $delimiter = '_')
595: {
596: $cacheKey = __FUNCTION__ . $delimiter;
597:
598: $result = static::_cache($cacheKey, $string);
599:
600: if ($result === false) {
601: $result = str_replace(' ', '', static::humanize($string, $delimiter));
602: static::_cache($cacheKey, $string, $result);
603: }
604:
605: return $result;
606: }
607:
608: 609: 610: 611: 612: 613: 614: 615: 616:
617: public static function underscore($string)
618: {
619: return static::delimit(str_replace('-', '_', $string), '_');
620: }
621:
622: 623: 624: 625: 626: 627: 628: 629:
630: public static function dasherize($string)
631: {
632: return static::delimit(str_replace('_', '-', $string), '-');
633: }
634:
635: 636: 637: 638: 639: 640: 641: 642: 643:
644: public static function humanize($string, $delimiter = '_')
645: {
646: $cacheKey = __FUNCTION__ . $delimiter;
647:
648: $result = static::_cache($cacheKey, $string);
649:
650: if ($result === false) {
651: $result = explode(' ', str_replace($delimiter, ' ', $string));
652: foreach ($result as &$word) {
653: $word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1);
654: }
655: $result = implode(' ', $result);
656: static::_cache($cacheKey, $string, $result);
657: }
658:
659: return $result;
660: }
661:
662: 663: 664: 665: 666: 667: 668:
669: public static function delimit($string, $delimiter = '_')
670: {
671: $cacheKey = __FUNCTION__ . $delimiter;
672:
673: $result = static::_cache($cacheKey, $string);
674:
675: if ($result === false) {
676: $result = mb_strtolower(preg_replace('/(?<=\\w)([A-Z])/', $delimiter . '\\1', $string));
677: static::_cache($cacheKey, $string, $result);
678: }
679:
680: return $result;
681: }
682:
683: 684: 685: 686: 687: 688: 689:
690: public static function tableize($className)
691: {
692: $result = static::_cache(__FUNCTION__, $className);
693:
694: if ($result === false) {
695: $result = static::pluralize(static::underscore($className));
696: static::_cache(__FUNCTION__, $className, $result);
697: }
698:
699: return $result;
700: }
701:
702: 703: 704: 705: 706: 707: 708:
709: public static function classify($tableName)
710: {
711: $result = static::_cache(__FUNCTION__, $tableName);
712:
713: if ($result === false) {
714: $result = static::camelize(static::singularize($tableName));
715: static::_cache(__FUNCTION__, $tableName, $result);
716: }
717:
718: return $result;
719: }
720:
721: 722: 723: 724: 725: 726: 727:
728: public static function variable($string)
729: {
730: $result = static::_cache(__FUNCTION__, $string);
731:
732: if ($result === false) {
733: $camelized = static::camelize(static::underscore($string));
734: $replace = strtolower(substr($camelized, 0, 1));
735: $result = $replace . substr($camelized, 1);
736: static::_cache(__FUNCTION__, $string, $result);
737: }
738:
739: return $result;
740: }
741:
742: 743: 744: 745: 746: 747: 748: 749: 750: 751:
752: public static function slug($string, $replacement = '-')
753: {
754: deprecationWarning(
755: 'Inflector::slug() is deprecated. ' .
756: 'Use Text::slug() instead.'
757: );
758: $quotedReplacement = preg_quote($replacement, '/');
759:
760: $map = [
761: '/[^\s\p{Zs}\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
762: '/[\s\p{Zs}]+/mu' => $replacement,
763: sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
764: ];
765:
766: $string = str_replace(
767: array_keys(static::$_transliteration),
768: static::$_transliteration,
769: $string
770: );
771:
772: return preg_replace(array_keys($map), array_values($map), $string);
773: }
774: }
775: