Overview

Packages

  • Support

Classes

  • Horde_Support_Array
  • Horde_Support_Backtrace
  • Horde_Support_CombineStream
  • Horde_Support_ConsistentHash
  • Horde_Support_Guid
  • Horde_Support_Inflector
  • Horde_Support_Memory
  • Horde_Support_Numerizer
  • Horde_Support_Numerizer_Locale_Base
  • Horde_Support_Numerizer_Locale_De
  • Horde_Support_Numerizer_Locale_Pt
  • Horde_Support_Randomid
  • Horde_Support_Stack
  • Horde_Support_StringStream
  • Horde_Support_Stub
  • Horde_Support_Timer
  • Horde_Support_Uuid
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde Inflector class.
  4:  *
  5:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * @todo Add the locale-bubbling pattern from
  8:  *       Horde_Date_Parser/Horde_Support_Numerizer
  9:  *
 10:  * @category   Horde
 11:  * @package    Support
 12:  * @license    http://www.horde.org/licenses/bsd
 13:  */
 14: class Horde_Support_Inflector
 15: {
 16:     /**
 17:      * Inflection cache
 18:      *
 19:      * @var array
 20:      */
 21:     protected $_cache = array();
 22: 
 23:     /**
 24:      * Rules for pluralizing English nouns.
 25:      *
 26:      * @var array
 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:      * Rules for singularizing English nouns.
 56:      *
 57:      * @var array
 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:      * An array of words with the same singular and plural spellings.
 96:      *
 97:      * @var array
 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:      * Constructor.
117:      *
118:      * Stores a map of the uncountable words for quicker checks.
119:      */
120:     public function __construct()
121:     {
122:         $this->_uncountables_keys = array_flip($this->_uncountables);
123:     }
124: 
125:     /**
126:      * Adds an uncountable word.
127:      *
128:      * @param string $word The uncountable word.
129:      */
130:     public function uncountable($word)
131:     {
132:         $this->_uncountables[] = $word;
133:         $this->_uncountables_keys[$word] = true;
134:     }
135: 
136:     /**
137:      * Singular English word to pluralize.
138:      *
139:      * @param string $word Word to pluralize.
140:      *
141:      * @return string Plural form of $word.
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:      * Plural English word to singularize.
165:      *
166:      * @param string $word Word to singularize.
167:      *
168:      * @return string Singular form of $word.
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:      * Camel-cases a word.
192:      *
193:      * @param string $word         The word to camel-case.
194:      * @param string $firstLetter  Whether to upper or lower case the first.
195:      *                             letter of each slash-separated section.
196:      *
197:      * @return string Camelized $word
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:      * Capitalizes all the words and replaces some characters in the string to
233:      * create a nicer looking title.
234:      *
235:      * Titleize is meant for creating pretty output.
236:      *
237:      * See:
238:      * - http://daringfireball.net/2008/05/title_case
239:      * - http://daringfireball.net/2008/08/title_case_update
240:      *
241:      * Examples:
242:      * 1. titleize("man from the boondocks") => "Man From The Boondocks"
243:      * 2. titleize("x-men: the last stand")  => "X Men: The Last Stand"
244:      */
245:     public function titleize($word)
246:     {
247:         throw new Exception('not implemented yet');
248:     }
249: 
250:     /**
251:      * The reverse of camelize().
252:      *
253:      * Makes an underscored form from the expression in the string.
254:      *
255:      * Examples:
256:      * 1. underscore("ActiveRecord")        => "active_record"
257:      * 2. underscore("ActiveRecord_Errors") => "active_record_errors"
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:      * Replaces underscores with dashes in the string.
271:      *
272:      * Example:
273:      * 1. dasherize("puni_puni") => "puni-puni"
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:      * Capitalizes the first word and turns underscores into spaces and strips
287:      * _id.
288:      *
289:      * Like titleize(), this is meant for creating pretty output.
290:      *
291:      * Examples:
292:      * 1. humanize("employee_salary") => "Employee salary"
293:      * 2. humanize("author_id")       => "Author"
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:      * Removes the module part from the expression in the string.
311:      *
312:      * Examples:
313:      * 1. demodulize("Fax_Job") => "Job"
314:      * 1. demodulize("User")    => "User"
315:      */
316:     public function demodulize($classNameInModule)
317:     {
318:         $result = explode('_', $classNameInModule);
319:         return array_pop($result);
320:     }
321: 
322:     /**
323:      * Creates the name of a table like Rails does for models to table names.
324:      *
325:      * This method uses the pluralize() method on the last word in the string.
326:      *
327:      * Examples:
328:      * 1. tableize("RawScaledScorer") => "raw_scaled_scorers"
329:      * 2. tableize("egg_and_ham")     => "egg_and_hams"
330:      * 3. tableize("fancyCategory")   => "fancy_categories"
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:      * Creates a class name from a table name like Rails does for table names
345:      * to models.
346:      *
347:      * Examples:
348:      * 1. classify("egg_and_hams") => "EggAndHam"
349:      * 2. classify("post")         => "Post"
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:         // classes use underscores instead of slashes for namespaces
359:         $result = str_replace('/', '_', $result);
360:         return $this->setCache($tableName, 'classify', $result);
361:     }
362: 
363:     /**
364:      * Creates a foreign key name from a class name.
365:      *
366:      * $separateClassNameAndIdWithUnderscore sets whether the method should put
367:      * '_' between the name and 'id'.
368:      *
369:      * Examples:
370:      * 1. foreignKey("Message")        => "message_id"
371:      * 2. foreignKey("Message", false) => "messageid"
372:      * 3. foreignKey("Fax_Job")        => "fax_job_id"
373:      */
374:     public function foreignKey($className, $separateClassNameAndIdWithUnderscore = true)
375:     {
376:         throw new Exception('not implemented yet');
377:     }
378: 
379:     /**
380:      * Turns a number into an ordinal string used to denote the position in an
381:      * ordered sequence such as 1st, 2nd, 3rd, 4th.
382:      *
383:      * Examples:
384:      * 1. ordinalize(1)      => "1st"
385:      * 2. ordinalize(2)      => "2nd"
386:      * 3. ordinalize(1002)   => "1002nd"
387:      * 4. ordinalize(1003)   => "1003rd"
388:      */
389:     public function ordinalize($number)
390:     {
391:         throw new Exception('not implemented yet');
392:     }
393: 
394:     /**
395:      * Clears the inflection cache.
396:      */
397:     public function clearCache()
398:     {
399:         $this->_cache = array();
400:     }
401: 
402:     /**
403:      * Retuns a cached inflection.
404:      *
405:      * @return string | false
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:      * Caches an inflection.
415:      *
416:      * @param string $word   The word being inflected.
417:      * @param string $rule   The inflection rule.
418:      * @param string $value  The inflected value of $word.
419:      *
420:      * @return string The inflected value
421:      */
422:     public function setCache($word, $rule, $value)
423:     {
424:         $this->_cache[$word . '|' . $rule] = $value;
425:         return $value;
426:     }
427: }
428: 
API documentation generated by ApiGen