1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: class Horde_Support_Inflector
15: {
16: 17: 18: 19: 20:
21: protected $_cache = array();
22:
23: 24: 25: 26: 27:
28: protected $_pluralizationRules = array(
29: '/move$/i' => 'moves',
30: '/sex$/i' => 'sexes',
31: '/child$/i' => 'children',
32: '/man$/i' => 'men',
33: '/foot$/i' => 'feet',
34: '/person$/i' => 'people',
35: '/(quiz)$/i' => '$1zes',
36: '/^(ox)$/i' => '$1en',
37: '/(m|l)ouse$/i' => '$1ice',
38: '/(matr|vert|ind)ix|ex$/i' => '$1ices',
39: '/(x|ch|ss|sh)$/i' => '$1es',
40: '/([^aeiouy]|qu)ies$/i' => '$1y',
41: '/([^aeiouy]|qu)y$/i' => '$1ies',
42: '/(?:([^f])fe|([lr])f)$/i' => '$1$2ves',
43: '/sis$/i' => 'ses',
44: '/([ti])um$/i' => '$1a',
45: '/(buffal|tomat)o$/i' => '$1oes',
46: '/(bu)s$/i' => '$1ses',
47: '/(alias|status)$/i' => '$1es',
48: '/(octop|vir)us$/i' => '$1i',
49: '/(ax|test)is$/i' => '$1es',
50: '/s$/i' => 's',
51: '/$/' => 's',
52: );
53:
54: 55: 56: 57: 58:
59: protected $_singularizationRules = array(
60: '/cookies$/i' => 'cookie',
61: '/moves$/i' => 'move',
62: '/sexes$/i' => 'sex',
63: '/children$/i' => 'child',
64: '/men$/i' => 'man',
65: '/feet$/i' => 'foot',
66: '/people$/i' => 'person',
67: '/databases$/i'=> 'database',
68: '/(quiz)zes$/i' => '\1',
69: '/(matr)ices$/i' => '\1ix',
70: '/(vert|ind)ices$/i' => '\1ex',
71: '/^(ox)en/i' => '\1',
72: '/(alias|status)es$/i' => '\1',
73: '/([octop|vir])i$/i' => '\1us',
74: '/(cris|ax|test)es$/i' => '\1is',
75: '/(shoe)s$/i' => '\1',
76: '/(o)es$/i' => '\1',
77: '/(bus)es$/i' => '\1',
78: '/([m|l])ice$/i' => '\1ouse',
79: '/(x|ch|ss|sh)es$/i' => '\1',
80: '/(m)ovies$/i' => '\1ovie',
81: '/(s)eries$/i' => '\1eries',
82: '/([^aeiouy]|qu)ies$/i' => '\1y',
83: '/([lr])ves$/i' => '\1f',
84: '/(tive)s$/i' => '\1',
85: '/(hive)s$/i' => '\1',
86: '/([^f])ves$/i' => '\1fe',
87: '/(^analy)ses$/i' => '\1sis',
88: '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
89: '/([ti])a$/i' => '\1um',
90: '/(n)ews$/i' => '\1ews',
91: '/(.*)s$/i' => '\1',
92: );
93:
94: 95: 96: 97: 98:
99: protected $_uncountables = array(
100: 'aircraft',
101: 'cannon',
102: 'deer',
103: 'equipment',
104: 'fish',
105: 'information',
106: 'money',
107: 'moose',
108: 'rice',
109: 'series',
110: 'sheep',
111: 'species',
112: 'swine',
113: );
114:
115: 116: 117: 118: 119:
120: public function __construct()
121: {
122: $this->_uncountables_keys = array_flip($this->_uncountables);
123: }
124:
125: 126: 127: 128: 129:
130: public function uncountable($word)
131: {
132: $this->_uncountables[] = $word;
133: $this->_uncountables_keys[$word] = true;
134: }
135:
136: 137: 138: 139: 140: 141: 142:
143: public function pluralize($word)
144: {
145: if ($plural = $this->getCache($word, 'pluralize')) {
146: return $plural;
147: }
148:
149: if (isset($this->_uncountables_keys[$word])) {
150: return $word;
151: }
152:
153: foreach ($this->_pluralizationRules as $regexp => $replacement) {
154: $plural = preg_replace($regexp, $replacement, $word, -1, $matches);
155: if ($matches > 0) {
156: return $this->setCache($word, 'pluralize', $plural);
157: }
158: }
159:
160: return $this->setCache($word, 'pluralize', $word);
161: }
162:
163: 164: 165: 166: 167: 168: 169:
170: public function singularize($word)
171: {
172: if ($singular = $this->getCache($word, 'singularize')) {
173: return $singular;
174: }
175:
176: if (isset($this->_uncountables_keys[$word])) {
177: return $word;
178: }
179:
180: foreach ($this->_singularizationRules as $regexp => $replacement) {
181: $singular = preg_replace($regexp, $replacement, $word, -1, $matches);
182: if ($matches > 0) {
183: return $this->setCache($word, 'singularize', $singular);
184: }
185: }
186:
187: return $this->setCache($word, 'singularize', $word);
188: }
189:
190: 191: 192: 193: 194: 195: 196: 197: 198:
199: public function camelize($word, $firstLetter = 'upper')
200: {
201: if ($camelized = $this->getCache($word, 'camelize' . $firstLetter)) {
202: return $camelized;
203: }
204:
205: $camelized = $word;
206: if (strtolower($camelized) != $camelized &&
207: strpos($camelized, '_') !== false) {
208: $camelized = str_replace('_', '/', $camelized);
209: }
210: if (strpos($camelized, '/') !== false) {
211: $camelized = str_replace('/', '/ ', $camelized);
212: }
213: if (strpos($camelized, '_') !== false) {
214: $camelized = strtr($camelized, '_', ' ');
215: }
216:
217: $camelized = str_replace(' ', '', ucwords($camelized));
218:
219: if ($firstLetter == 'lower') {
220: $parts = array();
221: foreach (explode('/', $camelized) as $part) {
222: $part[0] = strtolower($part[0]);
223: $parts[] = $part;
224: }
225: $camelized = implode('/', $parts);
226: }
227:
228: return $this->setCache($word, 'camelize' . $firstLetter, $camelized);
229: }
230:
231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244:
245: public function titleize($word)
246: {
247: throw new Exception('not implemented yet');
248: }
249:
250: 251: 252: 253: 254: 255: 256: 257: 258:
259: public function underscore($camelCasedWord)
260: {
261: $word = $camelCasedWord;
262: if ($result = $this->getCache($word, 'underscore')) {
263: return $result;
264: }
265: $result = strtolower(preg_replace('/([a-z])([A-Z])/', "\${1}_\${2}", $word));
266: return $this->setCache($word, 'underscore', $result);
267: }
268:
269: 270: 271: 272: 273: 274:
275: public function dasherize($underscoredWord)
276: {
277: if ($result = $this->getCache($underscoredWord, 'dasherize')) {
278: return $result;
279: }
280:
281: $result = str_replace('_', '-', $this->underscore($underscoredWord));
282: return $this->setCache($underscoredWord, 'dasherize', $result);
283: }
284:
285: 286: 287: 288: 289: 290: 291: 292: 293: 294:
295: public function humanize($lowerCaseAndUnderscoredWord)
296: {
297: $word = $lowerCaseAndUnderscoredWord;
298: if ($result = $this->getCache($word, 'humanize')) {
299: return $result;
300: }
301:
302: $result = ucfirst(str_replace('_', ' ', $this->underscore($word)));
303: if (substr($result, -3, 3) == ' id') {
304: $result = str_replace(' id', '', $result);
305: }
306: return $this->setCache($word, 'humanize', $result);
307: }
308:
309: 310: 311: 312: 313: 314: 315:
316: public function demodulize($classNameInModule)
317: {
318: $result = explode('_', $classNameInModule);
319: return array_pop($result);
320: }
321:
322: 323: 324: 325: 326: 327: 328: 329: 330: 331:
332: public function tableize($className)
333: {
334: if ($result = $this->getCache($className, 'tableize')) {
335: return $result;
336: }
337:
338: $result = $this->pluralize($this->underscore($className));
339: $result = str_replace('/', '_', $result);
340: return $this->setCache($className, 'tableize', $result);
341: }
342:
343: 344: 345: 346: 347: 348: 349: 350:
351: public function classify($tableName)
352: {
353: if ($result = $this->getCache($tableName, 'classify')) {
354: return $result;
355: }
356: $result = $this->camelize($this->singularize($tableName));
357:
358:
359: $result = str_replace('/', '_', $result);
360: return $this->setCache($tableName, 'classify', $result);
361: }
362:
363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373:
374: public function foreignKey($className, $separateClassNameAndIdWithUnderscore = true)
375: {
376: throw new Exception('not implemented yet');
377: }
378:
379: 380: 381: 382: 383: 384: 385: 386: 387: 388:
389: public function ordinalize($number)
390: {
391: throw new Exception('not implemented yet');
392: }
393:
394: 395: 396:
397: public function clearCache()
398: {
399: $this->_cache = array();
400: }
401:
402: 403: 404: 405: 406:
407: public function getCache($word, $rule)
408: {
409: return isset($this->_cache[$word . '|' . $rule]) ?
410: $this->_cache[$word . '|' . $rule] : false;
411: }
412:
413: 414: 415: 416: 417: 418: 419: 420: 421:
422: public function setCache($word, $rule, $value)
423: {
424: $this->_cache[$word . '|' . $rule] = $value;
425: return $value;
426: }
427: }
428: