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